diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 2a843320ca..0000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,61 +0,0 @@ -name-template: $NEXT_PATCH_VERSION -tag-template: $NEXT_PATCH_VERSION -categories: - - title: 💥 Breaking changes - labels: - - breaking - - title: 🚀 New features and improvements - labels: - - enhancement - - feat - - feature - - title: 🐛 Bug fixes - labels: - - bug - - fix - - title: 📦 Dependency updates - labels: - - dependencies - - deps - collapse-after: 15 - - title: 📝 Documentation updates - labels: - - documentation - - docs - - doc - - title: 🌐 Localization and translation - labels: - - localization - - title: 🌐 Community-related changes - labels: - - community - - title: 👻 Maintenance - labels: - - chore - - maintenance - - title: 🚦 Tests - labels: - - test - - title: ✍ Other changes -exclude-labels: - - skip-changelog - - invalid -template: | - $CHANGES - -autolabeler: - - label: 'documentation' - files: - - '*.md' - branch: - - '/docs{0,1}\/.+/' - - label: 'bug' - branch: - - '/fix\/.+/' - - label: 'deps' - branch: - - '/deps\/.+/' - - label: 'enhancement' - branch: - - '/feature\/.+/' - - '/feat\/.+/' diff --git a/.github/workflows/blazor.yml b/.github/workflows/blazor.yml deleted file mode 100644 index 1939d2b8db..0000000000 --- a/.github/workflows/blazor.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Build / Publish Blazor WebAssembly Project - -on: - workflow_dispatch: - - push: - branches: - - main - paths: - - "src/apps/blazor/**" - - "src/Directory.Packages.props" - - "src/Dockerfile.Blazor" - - pull_request: - branches: - - main - paths: - - "src/apps/blazor/**" - - "src/Directory.Packages.props" - - "src/Dockerfile.Blazor" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: setup dotnet - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.x - - name: restore dependencies - run: dotnet restore ./src/apps/blazor/client/Client.csproj - - name: build - run: dotnet build ./src/apps/blazor/client/Client.csproj --no-restore - - name: test - run: dotnet test ./src/apps/blazor/client/Client.csproj --no-build --verbosity normal - - publish: - needs: build - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v4 - - name: docker login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: build and publish to github container registry - working-directory: ./src/ - run: | - docker build -t ghcr.io/${{ github.repository_owner }}/blazor:latest -f Dockerfile.Blazor . - docker push ghcr.io/${{ github.repository_owner }}/blazor:latest diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index 7a88fcb9b4..0000000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Release Drafter - -on: - workflow_dispatch: - push: - branches: - - main - -permissions: - contents: read - -jobs: - update_release_draft: - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml deleted file mode 100644 index 4ee62a6ff5..0000000000 --- a/.github/workflows/nuget.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Publish Package to NuGet.org -on: - push: - branches: - - main - paths: - - "FSH.StarterKit.nuspec" -jobs: - publish: - name: publish nuget - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - name: checkout code - - uses: nuget/setup-nuget@v2 - name: setup nuget - with: - nuget-version: "latest" - nuget-api-key: ${{ secrets.NUGET_API_KEY }} - - name: generate package - run: nuget pack FSH.StarterKit.nuspec -NoDefaultExcludes - - name: publish package - run: nuget push *.nupkg -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate diff --git a/.github/workflows/webapi.yml b/.github/workflows/webapi.yml deleted file mode 100644 index a84e28f03a..0000000000 --- a/.github/workflows/webapi.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Build / Publish .NET WebAPI Project - -on: - workflow_dispatch: - - push: - branches: - - main - paths: - - "src/api/**" - - "src/Directory.Packages.props" - - pull_request: - branches: - - main - paths: - - "src/api/**" - - "src/Directory.Packages.props" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: setup dotnet - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.x - - name: restore dependencies - run: dotnet restore ./src/api/server/Server.csproj - - name: build - run: dotnet build ./src/api/server/Server.csproj --no-restore - - name: test - run: dotnet test ./src/api/server/Server.csproj --no-build --verbosity normal - - publish: - needs: build - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v4 - - name: setup dotnet - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.x - - name: docker login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: publish to github container registry - working-directory: ./src/api/server/ - run: | - dotnet publish -c Release -p:ContainerRepository=ghcr.io/${{ github.repository_owner}}/webapi -p:RuntimeIdentifier=linux-x64 - docker push ghcr.io/${{ github.repository_owner}}/webapi --all-tags diff --git a/.template.config/icon.png b/.template.config/icon.png deleted file mode 100644 index fd9fa41873..0000000000 Binary files a/.template.config/icon.png and /dev/null differ diff --git a/.template.config/ide.host.json b/.template.config/ide.host.json deleted file mode 100644 index c98f871c6d..0000000000 --- a/.template.config/ide.host.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/vs-2017.3.host", - "order": 0, - "icon": "icon.png" -} \ No newline at end of file diff --git a/.template.config/template.json b/.template.config/template.json deleted file mode 100644 index a5415e836e..0000000000 --- a/.template.config/template.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/template", - "author": "Mukesh Murugan", - "classifications": [ - "WebAPI", - "Clean Architecture", - "Boilerplate", - "ASP.NET Core", - "Starter Kit", - "Cloud", - "Web" - ], - "tags": { - "language": "C#", - "type": "project" - }, - "identity": "FullStackHero.NET.StarterKit", - "name": "FullStackHero .NET Starter Kit", - "description": "The best way to start a full-stack .NET 9 Web App.", - "shortName": "fsh", - "sourceName": "FSH.Starter", - "preferNameDirectory": true, - "sources": [ - { - "source": "./", - "target": "./", - "exclude": [ - ".template.config/**", - ".idea/**", - ".vscode/**", - ".vs/**", - ".github/**", - "templates/**/*", - "**/*.filelist", - "**/*.user", - "**/images", - "**/*.lock.json", - "*.nuspec" - ], - "rename": { - "README-template.md": "README.md" - } - } - ], - "primaryOutputs": [ - { - "path": "./src" - } - ], - "postActions": [ - { - "description": "restore webapi project dependencies", - "manualInstructions": [ - { - "text": "Run 'dotnet restore'" - } - ], - "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", - "continueOnError": false - } - ] -} \ No newline at end of file diff --git a/FSH.StarterKit.nuspec b/FSH.StarterKit.nuspec deleted file mode 100644 index a96e4b69f1..0000000000 --- a/FSH.StarterKit.nuspec +++ /dev/null @@ -1,24 +0,0 @@ - - - - FullStackHero.NET.StarterKit - FullStackHero .NET Starter Kit - 2.0.4-rc - Mukesh Murugan - The best way to start a full-stack Multi-tenant .NET 9 Web App. - en-US - ./content/LICENSE - 2024 - ./content/README.md - https://fullstackhero.net/dotnet-starter-kit/general/getting-started/ - - - - - cleanarchitecture clean architecture WebAPI mukesh codewithmukesh fullstackhero solution csharp - ./content/icon.png - - - - - \ No newline at end of file diff --git a/README-template.md b/README-template.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/README.md b/README.md deleted file mode 100644 index 7682ba1331..0000000000 --- a/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# FullStackHero .NET 9 Starter Kit 🚀 - -> With ASP.NET Core Web API & Blazor Client - -FullStackHero .NET Starter Kit is a starting point for your next `.NET 9 Clean Architecture` Solution that incorporates the most essential packages and features your projects will ever need including out-of-the-box Multi-Tenancy support. This project can save well over 200+ hours of development time for your team. - -![FullStackHero .NET Starter Kit](./assets/fullstackhero-dotnet-starter-kit.png) - -# Important - -This project is currently work in progress. The NuGet package is not yet available for v2. For now, you can fork this repository to try it out. [Follow @iammukeshm on X](https://x.com/iammukeshm) for project related updates. - -# Quick Start Guide - -As the project is still in beta, the NuGet packages are not yet available. You can try out the project by pulling the code directly from this repository. - -Prerequisites: - -- .NET 9 SDK installed. -- Visual Studio IDE. -- Docker Desktop. -- PostgreSQL instance running on your machine or docker container. - -Please follow the below instructions. - -1. Fork this repository to your local. -2. Open up the `./src/FSH.Starter.sln`. -3. This would up the FSH Starter solution which has 3 main components. - 1. Aspire Dashboard (set as the default project) - 2. Web API - 3. Blazor -4. Now we will have to set the connection string for the API. Navigate to `./src/api/server/appsettings.Development.json` and change the `ConnectionString` under `DatabaseOptions`. Save it. -5. Once that is done, run the application via Visual Studio, with Aspire as the default project. This will open up Aspire Dashboard at `https://localhost:7200/`. -6. API will be running at `https://localhost:7000/swagger/index.html`. -7. Blazor will be running at `https://localhost:7100/`. - -# 🔎 The Project - -# ✨ Technologies - -- .NET 9 -- Entity Framework Core 9 -- Blazor -- MediatR -- PostgreSQL -- Redis -- FluentValidation - -# 👨‍🚀 Architecture - -# 📬 Service Endpoints - -| Endpoint | Method | Description | -| -------- | ------ | ---------------- | -| `/token` | POST | Generates Token. | - -# 🧪 Running Locally - -# 🐳 Docker Support - -# ☁️ Deploying to AWS - -# 🤝 Contributing - -# 🍕 Community - -Thanks to the community who contribute to this repository! [Submit your PR and join the elite list!](CONTRIBUTING.md) - -[![FullStackHero .NET Starter Kit Contributors](https://contrib.rocks/image?repo=fullstackhero/dotnet-starter-kit "FullStackHero .NET Starter Kit Contributors")](https://github.com/fullstackhero/dotnet-starter-kit/graphs/contributors) - -# 📝 Notes - -## Add Migrations - -Navigate to `./api/server` and run the following EF CLI commands. - -```bash -dotnet ef migrations add "Add Identity Schema" --project .././migrations/postgresql/ --context IdentityDbContext -o Identity -dotnet ef migrations add "Add Tenant Schema" --project .././migrations/postgresql/ --context TenantDbContext -o Tenant -dotnet ef migrations add "Add Todo Schema" --project .././migrations/postgresql/ --context TodoDbContext -o Todo -dotnet ef migrations add "Add Catalog Schema" --project .././migrations/postgresql/ --context CatalogDbContext -o Catalog -``` - -## What's Pending? - -- Few Identity Endpoints -- Blazor Client -- File Storage Service -- NuGet Generation Pipeline -- Source Code Generation -- Searching / Sorting - -# ⚖️ LICENSE - -MIT © [fullstackhero](LICENSE) diff --git a/assets/fullstackhero-dotnet-starter-kit.png b/assets/fullstackhero-dotnet-starter-kit.png deleted file mode 100644 index d5ac1f26ff..0000000000 Binary files a/assets/fullstackhero-dotnet-starter-kit.png and /dev/null differ diff --git a/compose/.env b/compose/.env deleted file mode 100644 index 1226475d10..0000000000 --- a/compose/.env +++ /dev/null @@ -1,11 +0,0 @@ -#BASE_PATH=/mnt/c/docker-services/fsh-dotnet-starter-kit -BASE_PATH=. -############################################################################################################################################################################ -# API Services -############################################################################################################################################################################ -FSH_DOTNETSTARTERKIT_WEBAPI_IMAGE=ghcr.io/fullstackhero/webapi:latest - -############################################################################################################################################################################ -# Websites -############################################################################################################################################################################ -FSH_DOTNETSTARTERKIT_BLAZOR_IMAGE=ghcr.io/fullstackhero/blazor:latest diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml deleted file mode 100644 index 8d75bcb074..0000000000 --- a/compose/docker-compose.yml +++ /dev/null @@ -1,195 +0,0 @@ -version: "4" #on wsl linux replace 3.8 -name: fullstackhero #on wsl linux replace with export COMPOSE_PROJECT_NAME=fullstackhero before docker-compose up command - -services: - webapi: - image: ${FSH_DOTNETSTARTERKIT_WEBAPI_IMAGE} - pull_policy: always - container_name: webapi - networks: - - fullstackhero - environment: - ASPNETCORE_ENVIRONMENT: docker - ASPNETCORE_URLS: https://+:7000;http://+:5000 - ASPNETCORE_HTTPS_PORT: 7000 - ASPNETCORE_Kestrel__Certificates__Default__Password: password! - ASPNETCORE_Kestrel__Certificates__Default__Path: /https/cert.pfx - DatabaseOptions__ConnectionString: Server=postgres;Port=5433;Database=fullstackhero;User Id=pgadmin;Password=pgadmin - DatabaseOptions__Provider: postgresql - JwtOptions__Key: QsJbczCNysv/5SGh+U7sxedX8C07TPQPBdsnSDKZ/aE= - HangfireOptions__Username: admin - HangfireOptions__Password: Secure1234!Me - MailOptions__From: mukesh@fullstackhero.net - MailOptions__Host: smtp.ethereal.email - MailOptions__Port: 587 - MailOptions__UserName: sherman.oconnell47@ethereal.email - MailOptions__Password: KbuTCFv4J6Fy7256vh - MailOptions__DisplayName: Mukesh Murugan - CorsOptions__AllowedOrigins__0: http://localhost:5010 - CorsOptions__AllowedOrigins__1: http://localhost:7100 - CorsOptions__AllowedOrigins__2: https://localhost:7020 - OpenTelemetryOptions__Endpoint: http://otel-collector:4317 - RateLimitOptions__EnableRateLimiting: "false" - OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317 - OTEL_SERVICE_NAME: FSH.Starter.WebApi.Host - volumes: - - ~/.aspnet/https:/https:ro #on wsl linux - #- /mnt/c/Users/eduar/.aspnet/https:/https:ro - ports: - - 7000:7000 - - 5000:5000 - depends_on: - postgres: - condition: service_healthy - restart: on-failure - - blazor: - image: ${FSH_DOTNETSTARTERKIT_BLAZOR_IMAGE} - pull_policy: always - container_name: blazor - environment: - Frontend_FSHStarterBlazorClient_Settings__AppSettingsTemplate: /usr/share/nginx/html/appsettings.json.TEMPLATE - Frontend_FSHStarterBlazorClient_Settings__AppSettingsJson: /usr/share/nginx/html/appsettings.json - FSHStarterBlazorClient_ApiBaseUrl: https://localhost:7000 - ApiBaseUrl: https://localhost:7000 - networks: - - fullstackhero - entrypoint: [ - "/bin/sh", - "-c", - "envsubst < - $${Frontend_FSHStarterBlazorClient_Settings__AppSettingsTemplate} > - $${Frontend_FSHStarterBlazorClient_Settings__AppSettingsJson} && find - /usr/share/nginx/html -type f | xargs chmod +r && exec nginx -g - 'daemon off;'", - ] - volumes: - - ~/.aspnet/https:/https:ro - ports: - - 7100:80 - depends_on: - postgres: - condition: service_healthy - restart: on-failure - - postgres: - container_name: postgres - image: postgres:15-alpine - networks: - - fullstackhero - environment: - POSTGRES_USER: pgadmin - POSTGRES_PASSWORD: pgadmin - PGPORT: 5433 - ports: - - 5433:5433 - volumes: - - postgres-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U pgadmin"] - interval: 10s - timeout: 5s - retries: 5 - - prometheus: - image: prom/prometheus:latest - container_name: prometheus - restart: unless-stopped - networks: - - fullstackhero - volumes: - - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - - prometheus-data:/prometheus - ports: - - 9090:9090 - - grafana: - container_name: grafana - image: grafana/grafana:latest - user: "472" - environment: - GF_INSTALL_PLUGINS: "grafana-clock-panel,grafana-simple-json-datasource" - ports: - - 3000:3000 - volumes: - - grafana-data:/var/lib/grafana - - ./grafana/config/:/etc/grafana/ - - ./grafana/dashboards/:/var/lib/grafana/dashboards - depends_on: - - prometheus - restart: unless-stopped - networks: - - fullstackhero - - otel-collector: - image: otel/opentelemetry-collector-contrib:latest - container_name: otel-collector - command: --config /etc/otel/config.yaml - environment: - JAEGER_ENDPOINT: "jaeger:4317" - LOKI_ENDPOINT: "http://loki:3100/loki/api/v1/push" - volumes: - - $BASE_PATH/otel-collector/otel-config.yaml:/etc/otel/config.yaml - - $BASE_PATH/otel-collector/log:/log/otel - depends_on: - - jaeger - - loki - - prometheus - ports: - - 8888:8888 # Prometheus metrics exposed by the collector - - 8889:8889 # Prometheus metrics exporter (scrape endpoint) - - 13133:13133 # health_check extension - - "55679:55679" # ZPages extension - - 4317:4317 # OTLP gRPC receiver - - 4318:4318 # OTLP Http receiver (Protobuf) - networks: - - fullstackhero - - jaeger: - container_name: jaeger - image: jaegertracing/all-in-one:latest - command: --query.ui-config /etc/jaeger/jaeger-ui.json - environment: - - METRICS_STORAGE_TYPE=prometheus - - PROMETHEUS_SERVER_URL=http://prometheus:9090 - - COLLECTOR_OTLP_ENABLED=true - volumes: - - $BASE_PATH/jaeger/jaeger-ui.json:/etc/jaeger/jaeger-ui.json - depends_on: - - prometheus - ports: - - "16686:16686" - networks: - - fullstackhero - - loki: - container_name: loki - image: grafana/loki:3.1.0 - command: -config.file=/mnt/config/loki-config.yml - volumes: - - $BASE_PATH/loki/loki.yml:/mnt/config/loki-config.yml - ports: - - "3100:3100" - networks: - - fullstackhero - - node_exporter: - image: quay.io/prometheus/node-exporter:v1.5.0 - container_name: node_exporter - command: "--path.rootfs=/host" - pid: host - restart: unless-stopped - volumes: - - /proc:/host/proc:ro - - /sys:/host/sys:ro - - /:/rootfs:ro - networks: - - fullstackhero - -volumes: - postgres-data: - grafana-data: - prometheus-data: - -networks: - fullstackhero: diff --git a/compose/grafana/config/grafana.ini b/compose/grafana/config/grafana.ini deleted file mode 100644 index 4277397334..0000000000 --- a/compose/grafana/config/grafana.ini +++ /dev/null @@ -1,16 +0,0 @@ -[auth.anonymous] -enabled = true - -# Organization name that should be used for unauthenticated users -org_name = Main Org. - -# Role for unauthenticated users, other valid values are `Editor` and `Admin` -org_role = Admin - -# Hide the Grafana version text from the footer and help tooltip for unauthenticated users (default: false) -hide_version = true - -[dashboards] -default_home_dashboard_path = /var/lib/grafana/dashboards/aspnet-core.json - -min_refresh_interval = 1s \ No newline at end of file diff --git a/compose/grafana/config/provisioning/dashboards/default.yml b/compose/grafana/config/provisioning/dashboards/default.yml deleted file mode 100644 index d2f0a7ca80..0000000000 --- a/compose/grafana/config/provisioning/dashboards/default.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: 1 - -providers: -- name: 'Prometheus' - orgId: 1 - folder: '' - type: file - disableDeletion: false - editable: true - options: - path: /var/lib/grafana/dashboards \ No newline at end of file diff --git a/compose/grafana/config/provisioning/datasources/default.yml b/compose/grafana/config/provisioning/datasources/default.yml deleted file mode 100644 index 428d40e2ed..0000000000 --- a/compose/grafana/config/provisioning/datasources/default.yml +++ /dev/null @@ -1,69 +0,0 @@ -# config file version -apiVersion: 1 - -# list of datasources that should be deleted from the database -deleteDatasources: - - name: Prometheus - orgId: 1 - -# list of datasources to insert/update depending -# whats available in the database -datasources: -- name: Prometheus - type: prometheus - access: proxy - # Access mode - proxy (server in the UI) or direct (browser in the UI). - url: http://host.docker.internal:9090 - uid: prom - -- name: Loki - uid: loki - type: loki - access: proxy - url: http://loki:3100 - # allow users to edit datasources from the UI. - editable: true - jsonData: - derivedFields: - - datasourceUid: jaeger - matcherRegex: (?:"traceid"):"(\w+)" - name: TraceID - url: $${__value.raw} - -- name: Jaeger - type: jaeger - uid: jaeger - access: proxy - url: http://jaeger:16686 - readOnly: false - isDefault: false - # allow users to edit datasources from the UI. - editable: true - jsonData: - tracesToLogsV2: - # Field with an internal link pointing to a logs data source in Grafana. - # datasourceUid value must match the uid value of the logs data source. - datasourceUid: 'loki' - spanStartTimeShift: '1h' - spanEndTimeShift: '-1h' - tags: [{ key: 'service.names', value: 'service_name' }] - filterByTraceID: false - filterBySpanID: false - customQuery: true - query: '{$${__tags}} |="$${__trace.traceId}"' - tracesToMetrics: - datasourceUid: 'prom' - spanStartTimeShift: '1h' - spanEndTimeShift: '-1h' - tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }] - queries: - - name: 'Sample query' - query: 'sum(rate(traces_spanmetrics_latency_bucket{$$__tags}[5m]))' - nodeGraph: - enabled: true - traceQuery: - timeShiftEnabled: true - spanStartTimeShift: '1h' - spanEndTimeShift: '-1h' - spanBar: - type: 'None' \ No newline at end of file diff --git a/compose/grafana/dashboards/aspnet-core-endpoint.json b/compose/grafana/dashboards/aspnet-core-endpoint.json deleted file mode 100644 index 05b5496712..0000000000 --- a/compose/grafana/dashboards/aspnet-core-endpoint.json +++ /dev/null @@ -1,933 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "ASP.NET Core endpoint metrics from OpenTelemetry", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 19925, - "graphTooltip": 0, - "id": 10, - "links": [ - { - "asDropdown": false, - "icon": "dashboard", - "includeVars": false, - "keepTime": true, - "tags": [], - "targetBlank": false, - "title": " ASP.NET Core", - "tooltip": "", - "type": "link", - "url": "/d/KdDACDp4z/asp-net-core-metrics" - } - ], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "continuous-GrYlRd", - "seriesBy": "max" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "match": "null+nan", - "result": { - "index": 0, - "text": "0 ms" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "p50" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": false - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 40, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "legendFormat": "p50", - "range": true, - "refId": "p50" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.75, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p75", - "range": true, - "refId": "p75" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p90", - "range": true, - "refId": "p90" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p95", - "range": true, - "refId": "p95" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.98, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p98", - "range": true, - "refId": "p98" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p99", - "range": true, - "refId": "p99" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.999, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m])) by (le))", - "hide": false, - "legendFormat": "p99.9", - "range": true, - "refId": "p99.9" - } - ], - "title": "Requests Duration - $method $route", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic", - "seriesBy": "max" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "match": "null+nan", - "result": { - "index": 0, - "text": "0%" - } - }, - "type": "special" - } - ], - "max": 1, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "All" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "4XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "5XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 46, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\", http_response_status_code=~\"4..|5..\"}[5m]) or vector(0)) / sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m]))", - "legendFormat": "All", - "range": true, - "refId": "All" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\", http_response_status_code=~\"4..\"}[5m]) or vector(0)) / sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m]))", - "hide": false, - "legendFormat": "4XX", - "range": true, - "refId": "4XX" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\", http_response_status_code=~\"5..\"}[5m]) or vector(0)) / sum(rate(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[5m]))", - "hide": false, - "legendFormat": "5XX", - "range": true, - "refId": "5XX" - } - ], - "title": "Errors Rate - $method $route", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Requests" - }, - "properties": [ - { - "id": "custom.width", - "value": 300 - }, - { - "id": "custom.cellOptions", - "value": { - "mode": "gradient", - "type": "gauge" - } - }, - { - "id": "color", - "value": { - "mode": "continuous-YlRd" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Route" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "title": "", - "url": "/d/NagEsjE4z/asp-net-core-endpoint-details?var-route=${__data.fields.Route}&var-method=${__data.fields.Method}&${__url_time_range}" - } - ] - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hideTimeOverride": false, - "id": 44, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Value" - } - ] - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum by (error_type) (\r\n max_over_time(http_server_request_duration_seconds_count{http_route=\"$route\", http_request_method=\"$method\", error_type!=\"\"}[$__rate_interval])\r\n)", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{route}}", - "range": false, - "refId": "A" - } - ], - "title": "Unhandled Exceptions", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "method": false - }, - "indexByName": { - "Time": 0, - "Value": 2, - "error_type": 1 - }, - "renameByName": { - "Value": "Requests", - "error_type": "Exception", - "http_request_method": "Method", - "http_route": "Route" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 42, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "max" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum by (http_response_status_code) (\r\n max_over_time(http_server_request_duration_seconds_count{http_route=\"$route\", http_request_method=\"$method\"}[$__rate_interval])\r\n )", - "legendFormat": "Status {{http_response_status_code}}", - "range": true, - "refId": "A" - } - ], - "title": "Requests HTTP Status Code", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "green", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 13 - }, - "id": 48, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "max" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum by (url_scheme) (\r\n max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[$__rate_interval])\r\n )", - "legendFormat": "{{scheme}}", - "range": true, - "refId": "A" - } - ], - "title": "Requests Secured", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 13 - }, - "id": 50, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "max" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum by (method_route) (\r\n label_replace(max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route=\"$route\", http_request_method=\"$method\"}[$__rate_interval]), \"method_route\", \"http/$1\", \"network_protocol_version\", \"(.*)\")\r\n )", - "legendFormat": "{{protocol}}", - "range": true, - "refId": "A" - } - ], - "title": "Requests HTTP Protocol", - "type": "stat" - } - ], - "refresh": "", - "revision": 1, - "schemaVersion": 39, - "tags": [ - "dotnet", - "prometheus", - "aspnetcore" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "fullstackhero.api", - "value": "fullstackhero.api" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_active_requests,job)", - "hide": 0, - "includeAll": false, - "label": "Job", - "multi": false, - "name": "job", - "options": [], - "query": { - "query": "label_values(http_server_active_requests,job)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "host.docker.internal:5000", - "value": "host.docker.internal:5000" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_active_requests{job=~\"$job\"},instance)", - "hide": 0, - "includeAll": false, - "label": "Instance", - "multi": false, - "name": "instance", - "options": [], - "query": { - "query": "label_values(http_server_active_requests{job=~\"$job\"},instance)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "api/roles/", - "value": "api/roles/" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_request_duration_seconds_count,http_route)", - "description": "Route", - "hide": 0, - "includeAll": false, - "label": "Route", - "multi": false, - "name": "route", - "options": [], - "query": { - "query": "label_values(http_server_request_duration_seconds_count,http_route)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "GET", - "value": "GET" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_request_duration_seconds_count{http_route=~\"$route\"},http_request_method)", - "hide": 0, - "includeAll": false, - "label": "Method", - "multi": false, - "name": "method", - "options": [], - "query": { - "query": "label_values(http_server_request_duration_seconds_count{http_route=~\"$route\"},http_request_method)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "1s", - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "ASP.NET Core Endpoint", - "uid": "NagEsjE4j", - "version": 2, - "weekStart": "" -} \ No newline at end of file diff --git a/compose/grafana/dashboards/aspnet-core.json b/compose/grafana/dashboards/aspnet-core.json deleted file mode 100644 index a0d2aa1740..0000000000 --- a/compose/grafana/dashboards/aspnet-core.json +++ /dev/null @@ -1,1332 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "ASP.NET Core metrics from OpenTelemetry", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 19924, - "graphTooltip": 0, - "id": 9, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "continuous-GrYlRd", - "seriesBy": "max" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "match": "null+nan", - "result": { - "index": 1, - "text": "0 ms" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "p50" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": false - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 40, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "legendFormat": "p50", - "range": true, - "refId": "p50" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.75, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p75", - "range": true, - "refId": "p75" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p90", - "range": true, - "refId": "p90" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p95", - "range": true, - "refId": "p95" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.98, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p98", - "range": true, - "refId": "p98" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99", - "range": true, - "refId": "p99" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.999, sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99.9", - "range": true, - "refId": "p99.9" - } - ], - "title": "Requests Duration", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic", - "seriesBy": "max" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "match": "null+nan", - "result": { - "index": 1, - "text": "0%" - } - }, - "type": "special" - } - ], - "max": 1, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "All" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "4XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "5XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 47, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_response_status_code=~\"4..|5..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval]))", - "legendFormat": "All", - "range": true, - "refId": "All" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_response_status_code=~\"4..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "4XX", - "range": true, - "refId": "4XX" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\", http_response_status_code=~\"5..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_request_duration_seconds_bucket{job=\"$job\", instance=\"$instance\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "5XX", - "range": true, - "refId": "5XX" - } - ], - "title": "Errors Rate", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 9 - }, - "id": 49, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(kestrel_active_connections{job=\"$job\", instance=\"$instance\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Current Connections", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 9 - }, - "id": 55, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(http_server_active_requests{job=\"$job\", instance=\"$instance\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Current Requests", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "mappings": [], - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 9 - }, - "id": 58, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "value", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\"})", - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Total Requests", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-red", - "mode": "fixed" - }, - "mappings": [], - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 9 - }, - "id": 59, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "value", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", error_type!=\"\"})", - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Total Unhandled Exceptions", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "green", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 13 - }, - "id": 60, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "max" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum by (url_scheme) (\r\n max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\"}[$__rate_interval])\r\n )", - "legendFormat": "{{scheme}}", - "range": true, - "refId": "A" - } - ], - "title": "Requests Secured", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 13 - }, - "id": 42, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "max" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum by (method_route) (\r\n label_replace(max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\"}[$__rate_interval]), \"method_route\", \"http/$1\", \"network_protocol_version\", \"(.*)\")\r\n )", - "legendFormat": "{{protocol}}", - "range": true, - "refId": "A" - } - ], - "title": "Requests HTTP Protocol", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Requests" - }, - "properties": [ - { - "id": "custom.width", - "value": 300 - }, - { - "id": "custom.cellOptions", - "value": { - "mode": "gradient", - "type": "gauge" - } - }, - { - "id": "color", - "value": { - "mode": "continuous-BlPu" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Endpoint" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "Test", - "url": "/d/NagEsjE4z/asp-net-core-endpoint-details?var-route=${__data.fields.http_route}&var-method=${__data.fields.http_request_method}&${__url_time_range}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "http_route" - }, - "properties": [ - { - "id": "custom.hidden", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "http_request_method" - }, - "properties": [ - { - "id": "custom.hidden", - "value": true - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 17 - }, - "hideTimeOverride": false, - "id": 51, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Value" - } - ] - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": " topk(10,\r\n sum by (http_route, http_request_method, method_route) (\r\n label_join(max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route!=\"\"}[$__rate_interval]), \"method_route\", \" \", \"http_request_method\", \"http_route\")\r\n ))", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{route}}", - "range": false, - "refId": "A" - } - ], - "title": "Top 10 Requested Endpoints", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "method": false, - "route": false - }, - "indexByName": { - "Time": 0, - "Value": 4, - "method": 2, - "method_route": 3, - "route": 1 - }, - "renameByName": { - "Value": "Requests", - "method": "", - "method_route": "Endpoint", - "route": "" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Requests" - }, - "properties": [ - { - "id": "custom.width", - "value": 300 - }, - { - "id": "custom.cellOptions", - "value": { - "mode": "gradient", - "type": "gauge" - } - }, - { - "id": "color", - "value": { - "mode": "continuous-YlRd" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Endpoint" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "title": "", - "url": "/d/NagEsjE4z/asp-net-core-endpoint-details?var-route=${__data.fields.http_route}&var-method=${__data.fields.http_request_method}&${__url_time_range}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "http_route" - }, - "properties": [ - { - "id": "custom.hidden", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "http_request_method" - }, - "properties": [ - { - "id": "custom.hidden", - "value": true - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 17 - }, - "hideTimeOverride": false, - "id": 54, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Value" - } - ] - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": " topk(10,\r\n sum by (http_route, http_request_method, method_route) (\r\n label_join(max_over_time(http_server_request_duration_seconds_count{job=\"$job\", instance=\"$instance\", http_route!=\"\", error_type!=\"\"}[$__rate_interval]), \"method_route\", \" \", \"http_request_method\", \"http_route\")\r\n ))", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{route}}", - "range": false, - "refId": "A" - } - ], - "title": "Top 10 Unhandled Exception Endpoints", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "method": false - }, - "indexByName": { - "Time": 0, - "Value": 4, - "method": 2, - "method_route": 3, - "route": 1 - }, - "renameByName": { - "Value": "Requests", - "method": "", - "method_route": "Endpoint", - "route": "" - } - } - } - ], - "type": "table" - } - ], - "refresh": "", - "revision": 1, - "schemaVersion": 39, - "tags": [ - "dotnet", - "prometheus", - "aspnetcore" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "fullstackhero.api", - "value": "fullstackhero.api" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_active_requests,job)", - "hide": 0, - "includeAll": false, - "label": "Job", - "multi": false, - "name": "job", - "options": [], - "query": { - "query": "label_values(http_server_active_requests,job)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "host.docker.internal:5000", - "value": "host.docker.internal:5000" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_server_active_requests{job=~\"$job\"},instance)", - "hide": 0, - "includeAll": false, - "label": "Instance", - "multi": false, - "name": "instance", - "options": [], - "query": { - "query": "label_values(http_server_active_requests{job=~\"$job\"},instance)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "1s", - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "ASP.NET Core", - "uid": "KdDACDp4z", - "version": 2, - "weekStart": "" -} \ No newline at end of file diff --git a/compose/grafana/dashboards/dotnet-otel-dashboard.json b/compose/grafana/dashboards/dotnet-otel-dashboard.json deleted file mode 100644 index 1b179c6791..0000000000 --- a/compose/grafana/dashboards/dotnet-otel-dashboard.json +++ /dev/null @@ -1,2031 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "Shows ASP.NET metrics from OpenTelemetry NuGet", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 9, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 15, - "panels": [], - "title": "Process", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 90, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "system" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "user" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 11, - "x": 0, - "y": 1 - }, - "id": 19, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max", - "mean" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "irate(process_cpu_time{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "__auto", - "range": true, - "refId": "CPU Usage" - } - ], - "title": "CPU Usage", - "transformations": [ - { - "id": "labelsToFields", - "options": { - "keepLabels": [ - "state" - ], - "valueLabel": "state" - } - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 90, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 10, - "x": 11, - "y": 1 - }, - "id": 16, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max", - "mean" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_memory_usage{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "Memory Usage", - "range": true, - "refId": "Memory Usage" - } - ], - "title": "Memory Usage", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-green", - "value": null - }, - { - "color": "dark-yellow", - "value": 50 - }, - { - "color": "dark-orange", - "value": 100 - }, - { - "color": "dark-red", - "value": 150 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 3, - "x": 21, - "y": 1 - }, - "id": 12, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "value" - }, - "pluginVersion": "10.0.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_threads{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "Threads", - "range": true, - "refId": "Threads" - } - ], - "title": "Threads", - "type": "stat" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 2, - "panels": [], - "title": "Runtime", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd", - "seriesBy": "max" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 0, - "y": 11 - }, - "id": 6, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max", - "mean" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_gc_committed_memory_size{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "hide": false, - "legendFormat": "Committed Memory Size", - "range": true, - "refId": "Committed Memory Size" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_gc_committed_memory_size{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "Objects Size", - "range": true, - "refId": "Objects Size" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": "irate(process_runtime_dotnet_gc_committed_memory_size{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Allocations Size", - "range": true, - "refId": "Allocations Size" - } - ], - "title": "General Memory Usage", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "text", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 60, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 0, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "gen0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen1" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "loh" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "poh" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 8, - "y": 11 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_gc_heap_size{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "__auto", - "range": true, - "refId": "Heap Size" - } - ], - "title": "Heap Generations (bytes)", - "transformations": [ - { - "id": "labelsToFields", - "options": { - "keepLabels": [ - "generation" - ], - "valueLabel": "generation" - } - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "text", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 60, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 0, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "gen0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen1" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "loh" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "poh" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 16, - "y": 11 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_gc_heap_fragmentation_size{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "__auto", - "range": true, - "refId": "Heap Fragmentation" - } - ], - "title": "Heap Fragmentation (bytes)", - "transformations": [ - { - "id": "labelsToFields", - "options": { - "keepLabels": [ - "generation" - ], - "valueLabel": "generation" - } - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "green", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": -1, - "drawStyle": "bars", - "fillOpacity": 100, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 0, - "pointSize": 1, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "0": { - "color": "transparent", - "index": 0, - "text": "None" - } - }, - "type": "value" - } - ], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "gen0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen1" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "gen2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 0, - "y": 20 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "exemplar": false, - "expr": "idelta(process_runtime_dotnet_gc_collections_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "gc" - } - ], - "title": "GC Collections", - "transformations": [ - { - "id": "labelsToFields", - "options": { - "keepLabels": [ - "generation" - ], - "mode": "columns", - "valueLabel": "generation" - } - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-red", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 90, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 8, - "y": 20 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "increase(process_runtime_dotnet_exceptions_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "Exceptions", - "range": true, - "refId": "Exceptions" - } - ], - "title": "Exceptions", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "fixed" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 4, - "x": 16, - "y": 20 - }, - "id": 11, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_thread_pool_threads_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "ThreadPool Threads", - "range": true, - "refId": "ThreadPool Threads" - } - ], - "title": "ThreadPool Threads", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "fixed" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 4, - "x": 20, - "y": 20 - }, - "id": 17, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "max_over_time(process_runtime_dotnet_thread_pool_queue_length{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])", - "legendFormat": "ThreadPool Threads Queue Length", - "range": true, - "refId": "ThreadPool Threads Queue Length" - } - ], - "title": "ThreadPool Threads Queue Length", - "type": "stat" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 29 - }, - "id": 33, - "panels": [], - "title": "HTTP Server", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "continuous-GrYlRd", - "seriesBy": "max" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 30 - }, - "id": 40, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "legendFormat": "p50", - "range": true, - "refId": "p50" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.75, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p75", - "range": true, - "refId": "p75" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p90", - "range": true, - "refId": "p90" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.95, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p95", - "range": true, - "refId": "p95" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.98, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p98", - "range": true, - "refId": "p98" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99", - "range": true, - "refId": "p99" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.999, sum(rate(http_server_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99.9", - "range": true, - "refId": "p99.9" - } - ], - "title": "Responses Duration", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic", - "seriesBy": "max" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "All" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "4XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "5XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 30 - }, - "id": 47, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", http_status_code!~\"2..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval]))", - "legendFormat": "All", - "range": true, - "refId": "All" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", http_status_code=~\"4..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "4XX", - "range": true, - "refId": "4XX" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", http_status_code=~\"5..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_server_duration_count{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "5XX", - "range": true, - "refId": "5XX" - } - ], - "title": "Errors Rate", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 39 - }, - "id": 21, - "panels": [], - "repeat": "http_client_peer_name", - "repeatDirection": "h", - "title": "HTTP Client ($http_client_peer_name)", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "dark-green", - "mode": "continuous-GrYlRd", - "seriesBy": "max" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 40 - }, - "id": 23, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "legendFormat": "p50", - "range": true, - "refId": "p50" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.75, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p75", - "range": true, - "refId": "p75" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p90", - "range": true, - "refId": "p90" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.95, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p95", - "range": true, - "refId": "p95" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.98, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p98", - "range": true, - "refId": "p98" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99", - "range": true, - "refId": "p99" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "histogram_quantile(0.999, sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval])) by (le))", - "hide": false, - "legendFormat": "p99.9", - "range": true, - "refId": "p99.9" - } - ], - "title": "Requests Duration", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic", - "seriesBy": "max" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "All" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-orange", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "4XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "light-yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "5XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 40 - }, - "id": 25, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "min", - "max" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\", http_status_code!~\"2..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval]))", - "legendFormat": "All", - "range": true, - "refId": "All" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\", http_status_code=~\"4..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "4XX", - "range": true, - "refId": "4XX" - }, - { - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "editorMode": "code", - "expr": "sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\", http_status_code=~\"5..\"}[$__rate_interval]) or vector(0)) / sum(rate(http_client_duration_bucket{exported_job=\"$exported_job\", exported_instance=\"$exported_instance\", net_peer_name=\"$http_client_peer_name\"}[$__rate_interval]))", - "hide": false, - "legendFormat": "5XX", - "range": true, - "refId": "5XX" - } - ], - "title": "Errors Rate", - "type": "timeseries" - } - ], - "refresh": "1m", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "FST.TAG.Manager", - "value": "FST.TAG.Manager" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(process_runtime_dotnet_gc_collections_count,exported_job)", - "hide": 0, - "includeAll": false, - "label": "Job", - "multi": false, - "name": "exported_job", - "options": [], - "query": { - "query": "label_values(process_runtime_dotnet_gc_collections_count,exported_job)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "39230ae2-5527-47b3-b546-6f8d4cfc9ab0", - "value": "39230ae2-5527-47b3-b546-6f8d4cfc9ab0" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(process_runtime_dotnet_gc_collections_count{exported_job=~\"$exported_job\"},exported_instance)", - "hide": 0, - "includeAll": false, - "label": "Instance", - "multi": false, - "name": "exported_instance", - "options": [], - "query": { - "query": "label_values(process_runtime_dotnet_gc_collections_count{exported_job=~\"$exported_job\"},exported_instance)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "prom" - }, - "definition": "label_values(http_client_duration_bucket{exported_job=~\"$exported_job\",exported_instance=~\"$exported_instance\"},net_peer_name)", - "hide": 2, - "includeAll": true, - "label": "HTTP Client Pear Name", - "multi": false, - "name": "http_client_peer_name", - "options": [], - "query": { - "query": "label_values(http_client_duration_bucket{exported_job=~\"$exported_job\",exported_instance=~\"$exported_instance\"},net_peer_name)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 5, - "type": "query" - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "ASP.NET OTEL Metrics", - "uid": "bc47b423-0b3c-4538-8e20-f84f84deefe5", - "version": 6, - "weekStart": "" -} \ No newline at end of file diff --git a/compose/grafana/dashboards/logs-dashboard.json b/compose/grafana/dashboards/logs-dashboard.json deleted file mode 100644 index f4ddf3b973..0000000000 --- a/compose/grafana/dashboards/logs-dashboard.json +++ /dev/null @@ -1,334 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 6, - "panels": [], - "title": "Logs by Level", - "type": "row" - }, - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 1, - "drawStyle": "bars", - "fillOpacity": 100, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "stepBefore", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 3, - "interval": "1m", - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "editorMode": "code", - "expr": "sum by (level) (count_over_time({service_name=\"$service_name\", level=~\"$level\"} [$__interval]))", - "legendFormat": "{{level}}", - "queryType": "range", - "refId": "A" - } - ], - "title": "Log Volume", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 5, - "panels": [], - "title": "Logs Detailed Information", - "type": "row" - }, - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "gridPos": { - "h": 11, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 2, - "options": { - "dedupStrategy": "none", - "enableLogDetails": true, - "prettifyLogMessage": false, - "showCommonLabels": false, - "showLabels": false, - "showTime": true, - "sortOrder": "Descending", - "wrapLogMessage": false - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "editorMode": "code", - "expr": "{service_name=\"$service_name\", severity_text=~\"$level\"} |=\"$search\" | line_format `[{{ .severity_text }}] {{ .message_template_text }}`", - "queryType": "range", - "refId": "A" - } - ], - "title": "Logs", - "type": "logs" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 21 - }, - "id": 4, - "panels": [], - "title": "Logs with TraceId Link", - "type": "row" - }, - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "gridPos": { - "h": 13, - "w": 24, - "x": 0, - "y": 22 - }, - "id": 7, - "options": { - "dedupStrategy": "none", - "enableLogDetails": true, - "prettifyLogMessage": false, - "showCommonLabels": false, - "showLabels": false, - "showTime": false, - "sortOrder": "Descending", - "wrapLogMessage": false - }, - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "loki" - }, - "editorMode": "code", - "expr": "{service_name=\"$service_name\", level=~\"$level\"} |=\"$search\" | json ", - "key": "Q-b242453d-acff-49f2-9239-12ceaf57fa43-0", - "queryType": "range", - "refId": "A" - } - ], - "title": "Log Entries with Trace Link", - "type": "logs" - } - ], - "refresh": "", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "FSH.Starter.WebApi.Host", - "value": "FSH.Starter.WebApi.Host" - }, - "datasource": { - "type": "loki", - "uid": "loki" - }, - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Service", - "multi": false, - "name": "service_name", - "options": [], - "query": { - "label": "service_name", - "refId": "LokiVariableQueryEditor-VariableQuery", - "stream": "", - "type": 1 - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "loki", - "uid": "loki" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "level", - "options": [], - "query": { - "label": "severity_text", - "refId": "LokiVariableQueryEditor-VariableQuery", - "stream": "", - "type": 1 - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": { - "selected": false, - "text": "", - "value": "" - }, - "hide": 0, - "label": "Search Text", - "name": "search", - "options": [ - { - "selected": true, - "text": "", - "value": "" - } - ], - "query": "", - "skipUrlSync": false, - "type": "textbox" - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Logs", - "uid": "f4463c33-40c8-4def-aac2-95d365040f2e", - "version": 1, - "weekStart": "" -} \ No newline at end of file diff --git a/compose/grafana/dashboards/node-exporter.json b/compose/grafana/dashboards/node-exporter.json deleted file mode 100644 index cb734d8060..0000000000 --- a/compose/grafana/dashboards/node-exporter.json +++ /dev/null @@ -1,23870 +0,0 @@ -{ - "annotations": { - "list": [ - { - "$$hashKey": "object:1058", - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 1860, - "graphTooltip": 1, - "id": 8, - "links": [ - { - "icon": "external link", - "tags": [], - "targetBlank": true, - "title": "GitHub", - "type": "link", - "url": "https://github.com/rfmoz/grafana-dashboards" - }, - { - "icon": "external link", - "tags": [], - "targetBlank": true, - "title": "Grafana", - "type": "link", - "url": "https://grafana.com/grafana/dashboards/1860" - } - ], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 261, - "panels": [], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Quick CPU / Mem / Disk", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Resource pressure via PSI", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "links": [], - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "dark-yellow", - "value": 70 - }, - { - "color": "dark-red", - "value": 90 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 0, - "y": 1 - }, - "id": 323, - "options": { - "displayMode": "basic", - "maxVizHeight": 300, - "minVizHeight": 10, - "minVizWidth": 0, - "namePlacement": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "sizing": "auto", - "text": {}, - "valueMode": "color" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "irate(node_pressure_cpu_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "instant": true, - "intervalFactor": 1, - "legendFormat": "CPU", - "range": false, - "refId": "CPU some", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "irate(node_pressure_memory_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "Mem", - "range": false, - "refId": "Memory some", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "irate(node_pressure_io_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "I/O", - "range": false, - "refId": "I/O some", - "step": 240 - } - ], - "title": "Pressure", - "type": "bargauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Busy state of all CPU cores together", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 85 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 95 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 3, - "y": 1 - }, - "id": 20, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "100 * (1 - avg(rate(node_cpu_seconds_total{mode=\"idle\", instance=\"$node\"}[$__rate_interval])))", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "", - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "CPU Busy", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "System load over all CPU cores together", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 85 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 95 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 6, - "y": 1 - }, - "id": 155, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "scalar(node_load1{instance=\"$node\",job=\"$job\"}) * 100 / count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu))", - "format": "time_series", - "hide": false, - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "Sys Load", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Non available RAM memory", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 80 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 9, - "y": 1 - }, - "hideTimeOverride": false, - "id": 16, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "((node_memory_MemTotal_bytes{instance=\"$node\", job=\"$job\"} - node_memory_MemFree_bytes{instance=\"$node\", job=\"$job\"}) / node_memory_MemTotal_bytes{instance=\"$node\", job=\"$job\"}) * 100", - "format": "time_series", - "hide": true, - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "(1 - (node_memory_MemAvailable_bytes{instance=\"$node\", job=\"$job\"} / node_memory_MemTotal_bytes{instance=\"$node\", job=\"$job\"})) * 100", - "format": "time_series", - "hide": false, - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "B", - "step": 240 - } - ], - "title": "RAM Used", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Used Swap", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 10 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 25 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 12, - "y": 1 - }, - "id": 21, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "((node_memory_SwapTotal_bytes{instance=\"$node\",job=\"$job\"} - node_memory_SwapFree_bytes{instance=\"$node\",job=\"$job\"}) / (node_memory_SwapTotal_bytes{instance=\"$node\",job=\"$job\"})) * 100", - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "SWAP Used", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Used Root FS", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 80 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 15, - "y": 1 - }, - "id": 154, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "100 - ((node_filesystem_avail_bytes{instance=\"$node\",job=\"$job\",mountpoint=\"/\",fstype!=\"rootfs\"} * 100) / node_filesystem_size_bytes{instance=\"$node\",job=\"$job\",mountpoint=\"/\",fstype!=\"rootfs\"})", - "format": "time_series", - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "Root FS Used", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Total number of CPU cores", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 2, - "x": 18, - "y": 1 - }, - "id": 14, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu))", - "instant": true, - "legendFormat": "__auto", - "range": false, - "refId": "A" - } - ], - "title": "CPU Cores", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "System uptime", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 4, - "x": 20, - "y": 1 - }, - "hideTimeOverride": true, - "id": 15, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "node_time_seconds{instance=\"$node\",job=\"$job\"} - node_boot_time_seconds{instance=\"$node\",job=\"$job\"}", - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "Uptime", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Total RootFS", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 0, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 70 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 90 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 2, - "x": 18, - "y": 3 - }, - "id": 23, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "node_filesystem_size_bytes{instance=\"$node\",job=\"$job\",mountpoint=\"/\",fstype!=\"rootfs\"}", - "format": "time_series", - "hide": false, - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "RootFS Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Total RAM", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 0, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 2, - "x": 20, - "y": 3 - }, - "id": 75, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "node_memory_MemTotal_bytes{instance=\"$node\",job=\"$job\"}", - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "RAM Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Total SWAP", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 0, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 2, - "x": 22, - "y": 3 - }, - "id": 18, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "node_memory_SwapTotal_bytes{instance=\"$node\",job=\"$job\"}", - "instant": true, - "intervalFactor": 1, - "range": false, - "refId": "A", - "step": 240 - } - ], - "title": "SWAP Total", - "type": "stat" - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 5 - }, - "id": 263, - "panels": [], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Basic CPU / Mem / Net / Disk", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Basic CPU info", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "percent" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Busy Iowait" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Idle" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Busy Iowait" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Idle" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Busy System" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Busy User" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Busy Other" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 6 - }, - "id": 77, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true, - "width": 250 - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"system\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "hide": false, - "instant": false, - "intervalFactor": 1, - "legendFormat": "Busy System", - "range": true, - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"user\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Busy User", - "range": true, - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"iowait\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Busy Iowait", - "range": true, - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=~\".*irq\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Busy IRQs", - "range": true, - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode!='idle',mode!='user',mode!='system',mode!='iowait',mode!='irq',mode!='softirq'}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Busy Other", - "range": true, - "refId": "E", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"idle\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Idle", - "range": true, - "refId": "F", - "step": 240 - } - ], - "title": "CPU Basic", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Basic memory usage", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "SWAP Used" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap Used" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - }, - { - "id": "custom.stacking", - "value": { - "group": false, - "mode": "normal" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM Cache + Buffer" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Available" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#DEDAF7", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - }, - { - "id": "custom.stacking", - "value": { - "group": false, - "mode": "normal" - } - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 6 - }, - "id": 78, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_MemTotal_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "RAM Total", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_MemTotal_bytes{instance=\"$node\",job=\"$job\"} - node_memory_MemFree_bytes{instance=\"$node\",job=\"$job\"} - (node_memory_Cached_bytes{instance=\"$node\",job=\"$job\"} + node_memory_Buffers_bytes{instance=\"$node\",job=\"$job\"} + node_memory_SReclaimable_bytes{instance=\"$node\",job=\"$job\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "RAM Used", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Cached_bytes{instance=\"$node\",job=\"$job\"} + node_memory_Buffers_bytes{instance=\"$node\",job=\"$job\"} + node_memory_SReclaimable_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "RAM Cache + Buffer", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_MemFree_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "RAM Free", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "(node_memory_SwapTotal_bytes{instance=\"$node\",job=\"$job\"} - node_memory_SwapFree_bytes{instance=\"$node\",job=\"$job\"})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "SWAP Used", - "refId": "E", - "step": 240 - } - ], - "title": "Memory Basic", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Basic network info per interface", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bps" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Recv_bytes_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Recv_bytes_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Recv_drop_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Recv_drop_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Recv_errs_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Recv_errs_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CCA300", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_bytes_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_bytes_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_drop_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_drop_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_errs_eth2" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Trans_errs_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CCA300", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "recv_bytes_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "recv_drop_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "recv_drop_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#967302", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "recv_errs_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "recv_errs_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_bytes_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_bytes_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_drop_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_drop_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#967302", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_errs_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "trans_errs_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 13 - }, - "id": 74, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])*8", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "recv {{device}}", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])*8", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "trans {{device}} ", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic Basic", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Disk space used of all filesystems mounted", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 13 - }, - "id": 152, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "100 - ((node_filesystem_avail_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'} * 100) / node_filesystem_size_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{mountpoint}}", - "refId": "A", - "step": 240 - } - ], - "title": "Disk Space Used Basic", - "type": "timeseries" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 20 - }, - "id": 265, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "percentage", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 70, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "percent" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Idle - Waiting for something to happen" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Iowait - Waiting for I/O to complete" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Irq - Servicing interrupts" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Nice - Niced processes executing in user mode" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Softirq - Servicing softirqs" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Steal - Time spent in other operating systems when running in a virtualized environment" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCE2DE", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "System - Processes executing in kernel mode" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "User - Normal processes executing in user mode" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#5195CE", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 21 - }, - "id": 3, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 250 - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"system\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "System - Processes executing in kernel mode", - "range": true, - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"user\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "User - Normal processes executing in user mode", - "range": true, - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"nice\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Nice - Niced processes executing in user mode", - "range": true, - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum by(instance) (irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"iowait\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Iowait - Waiting for I/O to complete", - "range": true, - "refId": "E", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"irq\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Irq - Servicing interrupts", - "range": true, - "refId": "F", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"softirq\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Softirq - Servicing softirqs", - "range": true, - "refId": "G", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"steal\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Steal - Time spent in other operating systems when running in a virtualized environment", - "range": true, - "refId": "H", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum(irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\", mode=\"idle\"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}) by (cpu)))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Idle - Waiting for something to happen", - "range": true, - "refId": "J", - "step": 240 - } - ], - "title": "CPU", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap - Swap memory usage" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused - Free memory unassigned" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Hardware Corrupted - *./" - }, - "properties": [ - { - "id": "custom.stacking", - "value": { - "group": false, - "mode": "normal" - } - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 21 - }, - "id": 24, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_MemTotal_bytes{instance=\"$node\",job=\"$job\"} - node_memory_MemFree_bytes{instance=\"$node\",job=\"$job\"} - node_memory_Buffers_bytes{instance=\"$node\",job=\"$job\"} - node_memory_Cached_bytes{instance=\"$node\",job=\"$job\"} - node_memory_Slab_bytes{instance=\"$node\",job=\"$job\"} - node_memory_PageTables_bytes{instance=\"$node\",job=\"$job\"} - node_memory_SwapCached_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Apps - Memory used by user-space applications", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_PageTables_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "PageTables - Memory used to map between virtual and physical memory addresses", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_SwapCached_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "SwapCache - Memory that keeps track of pages that have been fetched from swap but not yet been modified", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Slab_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Slab - Memory used by the kernel to cache data structures for its own use (caches like inode, dentry, etc)", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Cached_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Cache - Parked file data (file content) cache", - "refId": "E", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Buffers_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Buffers - Block device (e.g. harddisk) cache", - "refId": "F", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_MemFree_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Unused - Free memory unassigned", - "refId": "G", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "(node_memory_SwapTotal_bytes{instance=\"$node\",job=\"$job\"} - node_memory_SwapFree_bytes{instance=\"$node\",job=\"$job\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Swap - Swap space used", - "refId": "H", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_HardwareCorrupted_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working", - "refId": "I", - "step": 240 - } - ], - "title": "Memory Stack", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bits out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bps" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "receive_packets_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "receive_packets_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "transmit_packets_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "transmit_packets_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 33 - }, - "id": 84, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])*8", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])*8", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 33 - }, - "id": 156, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_size_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'} - node_filesystem_avail_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{mountpoint}}", - "refId": "A", - "step": 240 - } - ], - "title": "Disk Space Used", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "IO read (-) / write (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "iops" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Read.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 45 - }, - "id": 229, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_reads_completed_total{instance=\"$node\",job=\"$job\",device=~\"$diskdevices\"}[$__rate_interval])", - "intervalFactor": 4, - "legendFormat": "{{device}} - Reads completed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_writes_completed_total{instance=\"$node\",job=\"$job\",device=~\"$diskdevices\"}[$__rate_interval])", - "intervalFactor": 1, - "legendFormat": "{{device}} - Writes completed", - "refId": "B", - "step": 240 - } - ], - "title": "Disk IOps", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes read (-) / write (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "Bps" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "io time" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*read*./" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byType", - "options": "time" - }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "hidden" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 45 - }, - "id": 42, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_read_bytes_total{instance=\"$node\",job=\"$job\",device=~\"$diskdevices\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{device}} - Successfully read bytes", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_written_bytes_total{instance=\"$node\",job=\"$job\",device=~\"$diskdevices\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{device}} - Successfully written bytes", - "refId": "B", - "step": 240 - } - ], - "title": "I/O Usage Read / Write", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "%util", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 40, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "io time" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byType", - "options": "time" - }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "hidden" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 57 - }, - "id": 127, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_io_time_seconds_total{instance=\"$node\",job=\"$job\",device=~\"$diskdevices\"} [$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{device}}", - "refId": "A", - "step": 240 - } - ], - "title": "I/O Utilization", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "percentage", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 70, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 3, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "max": 1, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/^Guest - /" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#5195ce", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/^GuestNice - /" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#c15c17", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 57 - }, - "id": 319, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum by(instance) (irate(node_cpu_guest_seconds_total{instance=\"$node\",job=\"$job\", mode=\"user\"}[1m])) / on(instance) group_left sum by (instance)((irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}[1m])))", - "hide": false, - "legendFormat": "Guest - Time spent running a virtual CPU for a guest operating system", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "sum by(instance) (irate(node_cpu_guest_seconds_total{instance=\"$node\",job=\"$job\", mode=\"nice\"}[1m])) / on(instance) group_left sum by (instance)((irate(node_cpu_seconds_total{instance=\"$node\",job=\"$job\"}[1m])))", - "hide": false, - "legendFormat": "GuestNice - Time spent running a niced guest (virtual CPU for guest operating system)", - "range": true, - "refId": "B" - } - ], - "title": "CPU spent seconds in guests (VMs)", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "CPU / Memory / Net / Disk", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 21 - }, - "id": 266, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 22 - }, - "id": 136, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Inactive_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Inactive - Memory which has been less recently used. It is more eligible to be reclaimed for other purposes", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Active_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Active - Memory that has been used more recently and usually not reclaimed unless absolutely necessary", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Active / Inactive", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*CommitLimit - *./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 22 - }, - "id": 135, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Committed_AS_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Committed_AS - Amount of memory presently allocated on the system", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_CommitLimit_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "CommitLimit - Amount of memory currently available to be allocated on the system", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Committed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 32 - }, - "id": 191, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Inactive_file_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Inactive_file - File-backed memory on inactive LRU list", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Inactive_anon_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Inactive_anon - Anonymous and swap cache on inactive LRU list, including tmpfs (shmem)", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Active_file_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Active_file - File-backed memory on active LRU list", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Active_anon_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Active_anon - Anonymous and swap cache on active least-recently-used (LRU) list, including tmpfs", - "refId": "D", - "step": 240 - } - ], - "title": "Memory Active / Inactive Detail", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 32 - }, - "id": 130, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Writeback_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Writeback - Memory which is actively being written back to disk", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_WritebackTmp_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "WritebackTmp - Memory used by FUSE for temporary writeback buffers", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Dirty_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Dirty - Memory which is waiting to get written back to the disk", - "refId": "C", - "step": 240 - } - ], - "title": "Memory Writeback and Dirty", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ShmemHugePages - Memory used by shared memory (shmem) and tmpfs allocated with huge pages" - }, - "properties": [ - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ShmemHugePages - Memory used by shared memory (shmem) and tmpfs allocated with huge pages" - }, - "properties": [ - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 42 - }, - "id": 138, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Mapped_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Mapped - Used memory in mapped pages files which have been mapped, such as libraries", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Shmem_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Shmem - Used shared memory (shared between several processes, thus including RAM disks)", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_ShmemHugePages_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "ShmemHugePages - Memory used by shared memory (shmem) and tmpfs allocated with huge pages", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_ShmemPmdMapped_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "ShmemPmdMapped - Amount of shared (shmem/tmpfs) memory backed by huge pages", - "refId": "D", - "step": 240 - } - ], - "title": "Memory Shared and Mapped", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 42 - }, - "id": 131, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_SUnreclaim_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "SUnreclaim - Part of Slab, that cannot be reclaimed on memory pressure", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_SReclaimable_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "SReclaimable - Part of Slab, that might be reclaimed, such as caches", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Slab", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 52 - }, - "id": 70, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_VmallocChunk_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "VmallocChunk - Largest contiguous block of vmalloc area which is free", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_VmallocTotal_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "VmallocTotal - Total size of vmalloc memory area", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_VmallocUsed_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "VmallocUsed - Amount of vmalloc area which is used", - "refId": "C", - "step": 240 - } - ], - "title": "Memory Vmalloc", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 52 - }, - "id": 159, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Bounce_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Bounce - Memory used for block device bounce buffers", - "refId": "A", - "step": 240 - } - ], - "title": "Memory Bounce", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Inactive *./" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 62 - }, - "id": 129, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_AnonHugePages_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "AnonHugePages - Memory in anonymous huge pages", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_AnonPages_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "AnonPages - Memory in user pages not backed by files", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Anonymous", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 62 - }, - "id": 160, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_KernelStack_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "KernelStack - Kernel memory stack. This is not reclaimable", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Percpu_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "PerCPU - Per CPU memory allocated dynamically by loadable modules", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Kernel / CPU", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "pages", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 72 - }, - "id": 140, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_HugePages_Free{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "HugePages_Free - Huge pages in the pool that are not yet allocated", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_HugePages_Rsvd{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "HugePages_Rsvd - Huge pages for which a commitment to allocate from the pool has been made, but no allocation has yet been made", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_HugePages_Surp{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "HugePages_Surp - Huge pages in the pool above the value in /proc/sys/vm/nr_hugepages", - "refId": "C", - "step": 240 - } - ], - "title": "Memory HugePages Counter", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 72 - }, - "id": 71, - "options": { - "legend": { - "calcs": [ - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_HugePages_Total{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "HugePages - Total size of the pool of huge pages", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Hugepagesize_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Hugepagesize - Huge Page size", - "refId": "B", - "step": 240 - } - ], - "title": "Memory HugePages Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 82 - }, - "id": 128, - "options": { - "legend": { - "calcs": [ - "mean", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_DirectMap1G_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "DirectMap1G - Amount of pages mapped as this size", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_DirectMap2M_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "DirectMap2M - Amount of pages mapped as this size", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_DirectMap4k_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "DirectMap4K - Amount of pages mapped as this size", - "refId": "C", - "step": 240 - } - ], - "title": "Memory DirectMap", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 82 - }, - "id": 137, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Unevictable_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Unevictable - Amount of unevictable memory that can't be swapped out for a variety of reasons", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_Mlocked_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "MLocked - Size of pages locked to memory using the mlock() system call", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Unevictable and MLocked", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 92 - }, - "id": 132, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_memory_NFS_Unstable_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "NFS Unstable - Memory in NFS pages sent to the server, but not yet committed to the storage", - "refId": "A", - "step": 240 - } - ], - "title": "Memory NFS", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Memory Meminfo", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 22 - }, - "id": 267, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "pages out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*out/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 23 - }, - "id": 176, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pgpgin{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pagesin - Page in operations", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pgpgout{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pagesout - Page out operations", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Pages In / Out", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "pages out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*out/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 23 - }, - "id": 22, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pswpin{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pswpin - Pages swapped in", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pswpout{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pswpout - Pages swapped out", - "refId": "B", - "step": 240 - } - ], - "title": "Memory Pages Swap In / Out", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "faults", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Apps" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#629E51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A437C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#CFFAFF", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RAM_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#806EB7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#2F575E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unused" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Pgfault - Page major and minor fault operations" - }, - "properties": [ - { - "id": "custom.fillOpacity", - "value": 0 - }, - { - "id": "custom.stacking", - "value": { - "group": false, - "mode": "normal" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 33 - }, - "id": 175, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 350 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pgfault{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pgfault - Page major and minor fault operations", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pgmajfault{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pgmajfault - Major page fault operations", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_pgfault{instance=\"$node\",job=\"$job\"}[$__rate_interval]) - irate(node_vmstat_pgmajfault{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Pgminfault - Minor page fault operations", - "refId": "C", - "step": 240 - } - ], - "title": "Memory Page Faults", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#99440A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Buffers" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#58140C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6D1F62", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Cached" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Committed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#508642", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Dirty" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Free" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#B7DBAB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Mapped" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "PageTables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Page_Tables" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Slab_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Swap_Cache" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C15C17", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#511749", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total RAM + Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#052B51", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total Swap" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "VmallocUsed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 33 - }, - "id": 307, - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_vmstat_oom_kill{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "oom killer invocations ", - "refId": "A", - "step": 240 - } - ], - "title": "OOM Killer", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Memory Vmstat", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 23 - }, - "id": 293, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "seconds", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Variation*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 260, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_estimated_error_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Estimated error in seconds", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_offset_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Time offset in between local system and reference clock", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_maxerror_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Maximum error in seconds", - "refId": "C", - "step": 240 - } - ], - "title": "Time Synchronized Drift", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 291, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_loop_time_constant{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Phase-locked loop time adjust", - "refId": "A", - "step": 240 - } - ], - "title": "Time PLL Adjust", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Variation*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 34 - }, - "id": 168, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_sync_status{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Is clock synchronized to a reliable server (1 = yes, 0 = no)", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_frequency_adjustment_ratio{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Local clock frequency adjustment", - "refId": "B", - "step": 240 - } - ], - "title": "Time Synchronized Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "seconds", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 34 - }, - "id": 294, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_tick_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Seconds between clock ticks", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_timex_tai_offset_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "International Atomic Time (TAI) offset", - "refId": "B", - "step": 240 - } - ], - "title": "Time Misc", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "System Timesync", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 24 - }, - "id": 312, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 73 - }, - "id": 62, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_procs_blocked{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Processes blocked waiting for I/O to complete", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_procs_running{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Processes in runnable state", - "refId": "B", - "step": 240 - } - ], - "title": "Processes Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Enable with --collector.processes argument on node-exporter", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 73 - }, - "id": 315, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_processes_state{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ state }}", - "refId": "A", - "step": 240 - } - ], - "title": "Processes State", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "forks / sec", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 83 - }, - "id": 148, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_forks_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Processes forks second", - "refId": "A", - "step": 240 - } - ], - "title": "Processes Forks", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "decbytes" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Max.*/" - }, - "properties": [ - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 83 - }, - "id": 149, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(process_virtual_memory_bytes{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Processes virtual memory size in bytes", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "process_resident_memory_max_bytes{instance=\"$node\",job=\"$job\"}", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Maximum amount of virtual memory available in bytes", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(process_virtual_memory_bytes{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Processes virtual memory size in bytes", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(process_virtual_memory_max_bytes{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Maximum amount of virtual memory available in bytes", - "refId": "D", - "step": 240 - } - ], - "title": "Processes Memory", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Enable with --collector.processes argument on node-exporter", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "PIDs limit" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F2495C", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 93 - }, - "id": 313, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_processes_pids{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Number of PIDs", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_processes_max_processes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "PIDs limit", - "refId": "B", - "step": 240 - } - ], - "title": "PIDs Number and Limit", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "seconds", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*waiting.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 93 - }, - "id": 305, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_schedstat_running_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{ cpu }} - seconds spent running a process", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_schedstat_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{ cpu }} - seconds spent by processing waiting for this CPU", - "refId": "B", - "step": 240 - } - ], - "title": "Process schedule stats Running / Waiting", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Enable with --collector.processes argument on node-exporter", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Threads limit" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F2495C", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 103 - }, - "id": 314, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_processes_threads{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Allocated threads", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_processes_max_threads{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Threads limit", - "refId": "B", - "step": 240 - } - ], - "title": "Threads Number and Limit", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "System Processes", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 25 - }, - "id": 269, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 8, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_context_switches_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Context switches", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_intr_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Interrupts", - "refId": "B", - "step": 240 - } - ], - "title": "Context Switches / Interrupts", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 7, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_load1{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 4, - "legendFormat": "Load 1m", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_load5{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 4, - "legendFormat": "Load 5m", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_load15{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 4, - "legendFormat": "Load 15m", - "refId": "C", - "step": 240 - } - ], - "title": "System Load", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "hertz" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Max" - }, - "properties": [ - { - "id": "custom.lineStyle", - "value": { - "dash": [ - 10, - 10 - ], - "fill": "dash" - } - }, - { - "id": "color", - "value": { - "fixedColor": "blue", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 10 - }, - { - "id": "custom.hideFrom", - "value": { - "legend": true, - "tooltip": false, - "viz": false - } - }, - { - "id": "custom.fillBelowTo", - "value": "Min" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Min" - }, - "properties": [ - { - "id": "custom.lineStyle", - "value": { - "dash": [ - 10, - 10 - ], - "fill": "dash" - } - }, - { - "id": "color", - "value": { - "fixedColor": "blue", - "mode": "fixed" - } - }, - { - "id": "custom.hideFrom", - "value": { - "legend": true, - "tooltip": false, - "viz": false - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 36 - }, - "id": 321, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "node_cpu_scaling_frequency_hertz{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{ cpu }}", - "range": true, - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "avg(node_cpu_scaling_frequency_max_hertz{instance=\"$node\",job=\"$job\"})", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Max", - "range": true, - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "avg(node_cpu_scaling_frequency_min_hertz{instance=\"$node\",job=\"$job\"})", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Min", - "range": true, - "refId": "C", - "step": 240 - } - ], - "title": "CPU Frequency Scaling", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "https://docs.kernel.org/accounting/psi.html", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Memory some" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Memory full" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "light-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "I/O some" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-blue", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "I/O full" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "light-blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 36 - }, - "id": 322, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "rate(node_pressure_cpu_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "CPU some", - "range": true, - "refId": "CPU some", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "rate(node_pressure_memory_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Memory some", - "range": true, - "refId": "Memory some", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "rate(node_pressure_memory_stalled_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Memory full", - "range": true, - "refId": "Memory full", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "rate(node_pressure_io_waiting_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "I/O some", - "range": true, - "refId": "I/O some", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "rate(node_pressure_io_stalled_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "I/O full", - "range": true, - "refId": "I/O full", - "step": 240 - } - ], - "title": "Pressure Stall Information", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Enable with --collector.interrupts argument on node-exporter", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Critical*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Max*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 46 - }, - "id": 259, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_interrupts_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ type }} - {{ info }}", - "refId": "A", - "step": 240 - } - ], - "title": "Interrupts Detail", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 46 - }, - "id": 306, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_schedstat_timeslices_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{ cpu }}", - "refId": "A", - "step": 240 - } - ], - "title": "Schedule timeslices executed by each cpu", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 56 - }, - "id": 151, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_entropy_available_bits{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Entropy available to random number generators", - "refId": "A", - "step": 240 - } - ], - "title": "Entropy", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "seconds", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 56 - }, - "id": 308, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(process_cpu_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Time spent", - "refId": "A", - "step": 240 - } - ], - "title": "CPU time spent in user and system contexts", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Max*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 66 - }, - "id": 64, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "process_max_fds{instance=\"$node\",job=\"$job\"}", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Maximum open file descriptors", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "process_open_fds{instance=\"$node\",job=\"$job\"}", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Open file descriptors", - "refId": "B", - "step": 240 - } - ], - "title": "File Descriptors", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "System Misc", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 304, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "temperature", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "celsius" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Critical*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Max*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 59 - }, - "id": 158, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_hwmon_temp_celsius{instance=\"$node\",job=\"$job\"} * on(chip) group_left(chip_name) node_hwmon_chip_names{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ chip_name }} {{ sensor }} temp", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_hwmon_temp_crit_alarm_celsius{instance=\"$node\",job=\"$job\"} * on(chip) group_left(chip_name) node_hwmon_chip_names{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ chip_name }} {{ sensor }} Critical Alarm", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_hwmon_temp_crit_celsius{instance=\"$node\",job=\"$job\"} * on(chip) group_left(chip_name) node_hwmon_chip_names{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ chip_name }} {{ sensor }} Critical", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_hwmon_temp_crit_hyst_celsius{instance=\"$node\",job=\"$job\"} * on(chip) group_left(chip_name) node_hwmon_chip_names{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ chip_name }} {{ sensor }} Critical Historical", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_hwmon_temp_max_celsius{instance=\"$node\",job=\"$job\"} * on(chip) group_left(chip_name) node_hwmon_chip_names{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ chip_name }} {{ sensor }} Max", - "refId": "E", - "step": 240 - } - ], - "title": "Hardware temperature monitor", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Max*./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 59 - }, - "id": 300, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_cooling_device_cur_state{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Current {{ name }} in {{ type }}", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_cooling_device_max_state{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Max {{ name }} in {{ type }}", - "refId": "B", - "step": 240 - } - ], - "title": "Throttle cooling device", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 69 - }, - "id": 302, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_power_supply_online{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ power_supply }} online", - "refId": "A", - "step": 240 - } - ], - "title": "Power supply", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Hardware Misc", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 27 - }, - "id": 296, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 46 - }, - "id": 297, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_systemd_socket_accepted_connections_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ name }} Connections", - "refId": "A", - "step": 240 - } - ], - "title": "Systemd Sockets", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Failed" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F2495C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Inactive" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FF9830", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Active" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#73BF69", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Deactivating" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FFCB7D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Activating" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#C8F2C2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 46 - }, - "id": 298, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_systemd_units{instance=\"$node\",job=\"$job\",state=\"activating\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Activating", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_systemd_units{instance=\"$node\",job=\"$job\",state=\"active\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Active", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_systemd_units{instance=\"$node\",job=\"$job\",state=\"deactivating\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Deactivating", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_systemd_units{instance=\"$node\",job=\"$job\",state=\"failed\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Failed", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_systemd_units{instance=\"$node\",job=\"$job\",state=\"inactive\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Inactive", - "refId": "E", - "step": 240 - } - ], - "title": "Systemd Units State", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Systemd", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 28 - }, - "id": 270, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The number (after merges) of I/O requests completed per second for the device", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "IO read (-) / write (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "iops" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Read.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 47 - }, - "id": 9, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_reads_completed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "intervalFactor": 4, - "legendFormat": "{{device}} - Reads completed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_writes_completed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "intervalFactor": 1, - "legendFormat": "{{device}} - Writes completed", - "refId": "B", - "step": 240 - } - ], - "title": "Disk IOps Completed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The number of bytes read from or written to the device per second", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes read (-) / write (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "Bps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Read.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 47 - }, - "id": 33, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_read_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 4, - "legendFormat": "{{device}} - Read bytes", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_written_bytes_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Written bytes", - "refId": "B", - "step": 240 - } - ], - "title": "Disk R/W Data", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The average time for requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "time. read (-) / write (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 30, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Read.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 57 - }, - "id": 37, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_read_time_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval]) / irate(node_disk_reads_completed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}} - Read wait time avg", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_write_time_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval]) / irate(node_disk_writes_completed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{device}} - Write wait time avg", - "refId": "B", - "step": 240 - } - ], - "title": "Disk Average Wait Time", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The average queue length of the requests that were issued to the device", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "aqu-sz", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 57 - }, - "id": 35, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_io_time_weighted_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}}", - "refId": "A", - "step": 240 - } - ], - "title": "Average Queue Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The number of read and write requests merged per second that were queued to the device", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "I/Os", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "iops" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Read.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 67 - }, - "id": 133, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_reads_merged_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "intervalFactor": 1, - "legendFormat": "{{device}} - Read merged", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_writes_merged_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "intervalFactor": 1, - "legendFormat": "{{device}} - Write merged", - "refId": "B", - "step": 240 - } - ], - "title": "Disk R/W Merged", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Percentage of elapsed time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100% for devices serving requests serially. But for devices serving requests in parallel, such as RAID arrays and modern SSDs, this number does not reflect their performance limits.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "%util", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 30, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 67 - }, - "id": 36, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_io_time_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}} - IO", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_discard_time_seconds_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}} - discard", - "refId": "B", - "step": 240 - } - ], - "title": "Time Spent Doing I/Os", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "The number of outstanding requests at the instant the sample was taken. Incremented as requests are given to appropriate struct request_queue and decremented as they finish.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "Outstanding req.", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 77 - }, - "id": 34, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_disk_io_now{instance=\"$node\",job=\"$job\"}", - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}} - IO now", - "refId": "A", - "step": 240 - } - ], - "title": "Instantaneous Queue Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "IOs", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "iops" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EAB839", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#6ED0E0", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EF843C", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#584477", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda2_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BA43A9", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sda3_.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F4D598", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#0A50A1", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#BF1B00", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdb3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0752D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#962D82", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#614D93", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdc3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#9AC48A", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#65C5DB", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9934E", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#EA6460", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde1.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E0F9D7", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sdd2.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#FCEACA", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*sde3.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F9E2D2", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 77 - }, - "id": 301, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_discards_completed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "intervalFactor": 4, - "legendFormat": "{{device}} - Discards completed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_disk_discards_merged_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{device}} - Discards merged", - "refId": "B", - "step": 240 - } - ], - "title": "Disk IOps Discards completed / merged", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Storage Disk", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 29 - }, - "id": 271, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 62 - }, - "id": 43, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_avail_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - Available", - "metric": "", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_free_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "hide": true, - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - Free", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_size_bytes{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "hide": true, - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - Size", - "refId": "C", - "step": 240 - } - ], - "title": "Filesystem space available", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "file nodes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 62 - }, - "id": 41, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_files_free{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - Free file nodes", - "refId": "A", - "step": 240 - } - ], - "title": "File Nodes Free", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "files", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 72 - }, - "id": 28, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filefd_maximum{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 4, - "legendFormat": "Max open files", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filefd_allocated{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Open files", - "refId": "B", - "step": 240 - } - ], - "title": "File Descriptor", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "file Nodes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 72 - }, - "id": 219, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_files{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - File nodes total", - "refId": "A", - "step": 240 - } - ], - "title": "File Nodes Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "/ ReadOnly" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 82 - }, - "id": 44, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_readonly{instance=\"$node\",job=\"$job\",device!~'rootfs'}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - ReadOnly", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_filesystem_device_error{instance=\"$node\",job=\"$job\",device!~'rootfs',fstype!~'tmpfs'}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{mountpoint}} - Device error", - "refId": "B", - "step": 240 - } - ], - "title": "Filesystem in ReadOnly / Error", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Storage Filesystem", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 30 - }, - "id": 272, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "receive_packets_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "receive_packets_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "transmit_packets_eth0" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#7EB26D", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "transmit_packets_lo" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#E24D42", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 47 - }, - "id": 60, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_packets_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_packets_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic by Packets", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 47 - }, - "id": 142, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_errs_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive errors", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_errs_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit errors", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic Errors", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 57 - }, - "id": 143, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_drop_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive drop", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_drop_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit drop", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic Drop", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 57 - }, - "id": 141, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_compressed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive compressed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_compressed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit compressed", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic Compressed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 67 - }, - "id": 146, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_multicast_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive multicast", - "refId": "A", - "step": 240 - } - ], - "title": "Network Traffic Multicast", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 67 - }, - "id": 144, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_fifo_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive fifo", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_fifo_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit fifo", - "refId": "B", - "step": 240 - } - ], - "title": "Network Traffic Fifo", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "pps" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 77 - }, - "id": 145, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_receive_frame_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{device}} - Receive frame", - "refId": "A", - "step": 240 - } - ], - "title": "Network Traffic Frame", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 77 - }, - "id": 231, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_carrier_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Statistic transmit_carrier", - "refId": "A", - "step": 240 - } - ], - "title": "Network Traffic Carrier", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Trans.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 87 - }, - "id": 232, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_network_transmit_colls_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{device}} - Transmit colls", - "refId": "A", - "step": 240 - } - ], - "title": "Network Traffic Colls", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "entries", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "NF conntrack limit" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 87 - }, - "id": 61, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_nf_conntrack_entries{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "NF conntrack entries", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_nf_conntrack_entries_limit{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "NF conntrack limit", - "refId": "B", - "step": 240 - } - ], - "title": "NF Conntrack", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "Entries", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 97 - }, - "id": 230, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_arp_entries{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ device }} - ARP entries", - "refId": "A", - "step": 240 - } - ], - "title": "ARP Entries", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 97 - }, - "id": 288, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_network_mtu_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ device }} - Bytes", - "refId": "A", - "step": 240 - } - ], - "title": "MTU", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 107 - }, - "id": 280, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_network_speed_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ device }} - Speed", - "refId": "A", - "step": 240 - } - ], - "title": "Speed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packets", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 107 - }, - "id": 289, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_network_transmit_queue_length{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ device }} - Interface transmit queue length", - "refId": "A", - "step": 240 - } - ], - "title": "Queue Length", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "packetes drop (-) / process (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Dropped.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 117 - }, - "id": 290, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_softnet_processed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{cpu}} - Processed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_softnet_dropped_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{cpu}} - Dropped", - "refId": "B", - "step": 240 - } - ], - "title": "Softnet Packets", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 117 - }, - "id": 310, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_softnet_times_squeezed_total{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "CPU {{cpu}} - Squeezed", - "refId": "A", - "step": 240 - } - ], - "title": "Softnet Out of Quota", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 127 - }, - "id": 309, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_network_up{operstate=\"up\",instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{interface}} - Operational state UP", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_network_carrier{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "instant": false, - "legendFormat": "{{device}} - Physical link state", - "refId": "B" - } - ], - "title": "Network Operational Status", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Network Traffic", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 31 - }, - "id": 273, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 48 - }, - "id": 63, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_alloc{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCP_alloc - Allocated sockets", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_inuse{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCP_inuse - Tcp sockets currently in use", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_mem{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCP_mem - Used memory for tcp", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_orphan{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCP_orphan - Orphan sockets", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_tw{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCP_tw - Sockets waiting close", - "refId": "E", - "step": 240 - } - ], - "title": "Sockstat TCP", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 48 - }, - "id": 124, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_UDPLITE_inuse{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "UDPLITE_inuse - Udplite sockets currently in use", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_UDP_inuse{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "UDP_inuse - Udp sockets currently in use", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_UDP_mem{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "UDP_mem - Used memory for udp", - "refId": "C", - "step": 240 - } - ], - "title": "Sockstat UDP", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 58 - }, - "id": 125, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_FRAG_inuse{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "FRAG_inuse - Frag sockets currently in use", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_RAW_inuse{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "RAW_inuse - Raw sockets currently in use", - "refId": "C", - "step": 240 - } - ], - "title": "Sockstat FRAG / RAW", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "bytes", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 58 - }, - "id": 220, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_TCP_mem_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "mem_bytes - TCP sockets in that state", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_UDP_mem_bytes{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "mem_bytes - UDP sockets in that state", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_FRAG_memory{instance=\"$node\",job=\"$job\"}", - "interval": "", - "intervalFactor": 1, - "legendFormat": "FRAG_memory - Used memory for frag", - "refId": "C" - } - ], - "title": "Sockstat Memory Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "sockets", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 68 - }, - "id": 126, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_sockstat_sockets_used{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Sockets_used - Sockets currently in use", - "refId": "A", - "step": 240 - } - ], - "title": "Sockstat Used", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Network Sockstat", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 32 - }, - "id": 274, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "octets out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Out.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 33 - }, - "id": 221, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_IpExt_InOctets{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "InOctets - Received octets", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_IpExt_OutOctets{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "OutOctets - Sent octets", - "refId": "B", - "step": 240 - } - ], - "title": "Netstat IP In / Out Octets", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "datagrams", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 33 - }, - "id": 81, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "width": 300 - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Ip_Forwarding{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "Forwarding - IP forwarding", - "refId": "A", - "step": 240 - } - ], - "title": "Netstat IP Forwarding", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "messages out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Out.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 43 - }, - "id": 115, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Icmp_InMsgs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "InMsgs - Messages which the entity received. Note that this counter includes all those counted by icmpInErrors", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Icmp_OutMsgs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "OutMsgs - Messages which this entity attempted to send. Note that this counter includes all those counted by icmpOutErrors", - "refId": "B", - "step": 240 - } - ], - "title": "ICMP In / Out", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "messages out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Out.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 43 - }, - "id": 50, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Icmp_InErrors{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "InErrors - Messages which the entity received but determined as having ICMP-specific errors (bad ICMP checksums, bad length, etc.)", - "refId": "A", - "step": 240 - } - ], - "title": "ICMP Errors", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "datagrams out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Out.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Snd.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 53 - }, - "id": 55, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_InDatagrams{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "InDatagrams - Datagrams received", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_OutDatagrams{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "OutDatagrams - Datagrams sent", - "refId": "B", - "step": 240 - } - ], - "title": "UDP In / Out", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "datagrams", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 53 - }, - "id": 109, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_InErrors{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "InErrors - UDP Datagrams that could not be delivered to an application", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_NoPorts{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "NoPorts - UDP Datagrams received on a port with no listener", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_UdpLite_InErrors{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "legendFormat": "InErrors Lite - UDPLite Datagrams that could not be delivered to an application", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_RcvbufErrors{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "RcvbufErrors - UDP buffer errors received", - "refId": "D", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Udp_SndbufErrors{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "SndbufErrors - UDP buffer errors send", - "refId": "E", - "step": 240 - } - ], - "title": "UDP Errors", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "datagrams out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Out.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/.*Snd.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 63 - }, - "id": 299, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_InSegs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "InSegs - Segments received, including those received in error. This count includes segments received on currently established connections", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_OutSegs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "OutSegs - Segments sent, including those on current connections but excluding those containing only retransmitted octets", - "refId": "B", - "step": 240 - } - ], - "title": "TCP In / Out", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 63 - }, - "id": 104, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_ListenOverflows{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "ListenOverflows - Times the listen queue of a socket overflowed", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_ListenDrops{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "ListenDrops - SYNs to LISTEN sockets ignored", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_TCPSynRetrans{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "TCPSynRetrans - SYN-SYN/ACK retransmits to break down retransmissions in SYN, fast/timeout retransmits", - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_RetransSegs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "legendFormat": "RetransSegs - Segments retransmitted - that is, the number of TCP segments transmitted containing one or more previously transmitted octets", - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_InErrs{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "legendFormat": "InErrs - Segments received in error (e.g., bad TCP checksums)", - "refId": "E" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_OutRsts{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "interval": "", - "legendFormat": "OutRsts - Segments sent with RST flag", - "refId": "F" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "irate(node_netstat_TcpExt_TCPRcvQDrop{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "legendFormat": "TCPRcvQDrop - Packets meant to be queued in rcv queue but dropped because socket rcvbuf limit hit", - "range": true, - "refId": "G" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "irate(node_netstat_TcpExt_TCPOFOQueue{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "hide": false, - "interval": "", - "legendFormat": "TCPOFOQueue - TCP layer receives an out of order packet and has enough memory to queue it", - "range": true, - "refId": "H" - } - ], - "title": "TCP Errors", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "connections", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*MaxConn *./" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#890F02", - "mode": "fixed" - } - }, - { - "id": "custom.fillOpacity", - "value": 0 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 73 - }, - "id": 85, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_netstat_Tcp_CurrEstab{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "CurrEstab - TCP connections for which the current state is either ESTABLISHED or CLOSE- WAIT", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_netstat_Tcp_MaxConn{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "MaxConn - Limit on the total number of TCP connections the entity can support (Dynamic is \"-1\")", - "refId": "B", - "step": 240 - } - ], - "title": "TCP Connections", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter out (-) / in (+)", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*Sent.*/" - }, - "properties": [ - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 73 - }, - "id": 91, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_SyncookiesFailed{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "SyncookiesFailed - Invalid SYN cookies received", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_SyncookiesRecv{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "SyncookiesRecv - SYN cookies received", - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_TcpExt_SyncookiesSent{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "SyncookiesSent - SYN cookies sent", - "refId": "C", - "step": 240 - } - ], - "title": "TCP SynCookie", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "connections", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 83 - }, - "id": 82, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_ActiveOpens{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "ActiveOpens - TCP connections that have made a direct transition to the SYN-SENT state from the CLOSED state", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "irate(node_netstat_Tcp_PassiveOpens{instance=\"$node\",job=\"$job\"}[$__rate_interval])", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "PassiveOpens - TCP connections that have made a direct transition to the SYN-RCVD state from the LISTEN state", - "refId": "B", - "step": 240 - } - ], - "title": "TCP Direct Transition", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "Enable with --collector.tcpstat argument on node-exporter", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "connections", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 83 - }, - "id": 320, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "node_tcp_connection_states{state=\"established\",instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "established - TCP sockets in established state", - "range": true, - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "node_tcp_connection_states{state=\"fin_wait2\",instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "fin_wait2 - TCP sockets in fin_wait2 state", - "range": true, - "refId": "B", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "node_tcp_connection_states{state=\"listen\",instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "listen - TCP sockets in listen state", - "range": true, - "refId": "C", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "editorMode": "code", - "expr": "node_tcp_connection_states{state=\"time_wait\",instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "time_wait - TCP sockets in time_wait state", - "range": true, - "refId": "D", - "step": 240 - } - ], - "title": "TCP Stat", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Network Netstat", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 33 - }, - "id": 279, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "seconds", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "normal" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 66 - }, - "id": 40, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_scrape_collector_duration_seconds{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{collector}} - Scrape duration", - "refId": "A", - "step": 240 - } - ], - "title": "Node Exporter Scrape Time", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "counter", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byRegexp", - "options": "/.*error.*/" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "#F2495C", - "mode": "fixed" - } - }, - { - "id": "custom.transform", - "value": "negative-Y" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 66 - }, - "id": 157, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "9.2.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_scrape_collector_success{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{collector}} - Scrape success", - "refId": "A", - "step": 240 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "expr": "node_textfile_scrape_error{instance=\"$node\",job=\"$job\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{collector}} - Scrape textfile error (1 = true)", - "refId": "B", - "step": 240 - } - ], - "title": "Node Exporter Scrape", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "000000001" - }, - "refId": "A" - } - ], - "title": "Node Exporter", - "type": "row" - } - ], - "refresh": "1m", - "revision": 1, - "schemaVersion": 39, - "tags": [ - "linux" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": "Datasource", - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "current": { - "selected": false, - "text": "node-exporter", - "value": "node-exporter" - }, - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Job", - "multi": false, - "name": "job", - "options": [], - "query": { - "query": "label_values(node_uname_info, job)", - "refId": "Prometheus-job-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "current": { - "selected": false, - "text": "node_exporter:9100", - "value": "node_exporter:9100" - }, - "datasource": { - "type": "prometheus", - "uid": "${datasource}" - }, - "definition": "label_values(node_uname_info{job=\"$job\"}, instance)", - "hide": 0, - "includeAll": false, - "label": "Host", - "multi": false, - "name": "node", - "options": [], - "query": { - "query": "label_values(node_uname_info{job=\"$job\"}, instance)", - "refId": "Prometheus-node-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "current": { - "selected": false, - "text": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+", - "value": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+" - }, - "hide": 2, - "includeAll": false, - "multi": false, - "name": "diskdevices", - "options": [ - { - "selected": true, - "text": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+", - "value": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+" - } - ], - "query": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+", - "skipUrlSync": false, - "type": "custom" - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "browser", - "title": "Node Exporter Full", - "uid": "rYdddlPWk", - "version": 3, - "weekStart": "" -} \ No newline at end of file diff --git a/compose/jaeger/jaeger-ui.json b/compose/jaeger/jaeger-ui.json deleted file mode 100644 index 0f06f2fcda..0000000000 --- a/compose/jaeger/jaeger-ui.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "monitor": { - "menuEnabled": true - }, - "dependencies": { - "menuEnabled": true - } - } \ No newline at end of file diff --git a/compose/loki/loki.yml b/compose/loki/loki.yml deleted file mode 100644 index a63d16c7ff..0000000000 --- a/compose/loki/loki.yml +++ /dev/null @@ -1,44 +0,0 @@ -auth_enabled: false - -limits_config: - allow_structured_metadata: true - -server: - http_listen_port: 3100 - grpc_listen_port: 9096 - -common: - instance_addr: localhost - path_prefix: /tmp/loki - storage: - filesystem: - chunks_directory: /tmp/loki/chunks - rules_directory: /tmp/loki/rules - replication_factor: 1 - ring: - kvstore: - store: inmemory - -query_range: - results_cache: - cache: - embedded_cache: - enabled: true - max_size_mb: 100 - -schema_config: - configs: - - from: 2020-10-24 - store: tsdb - object_store: filesystem - schema: v13 - index: - prefix: index_ - period: 24h - -storage_config: - boltdb: - directory: /tmp/loki/index - - filesystem: - directory: /tmp/loki/chunks diff --git a/compose/otel-collector/otel-config.yaml b/compose/otel-collector/otel-config.yaml deleted file mode 100644 index 191edae04c..0000000000 --- a/compose/otel-collector/otel-config.yaml +++ /dev/null @@ -1,78 +0,0 @@ -extensions: - health_check: - zpages: - endpoint: 0.0.0.0:55679 - -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 - zipkin: - endpoint: 0.0.0.0:9411 - -processors: - batch: - - resource: - attributes: - - action: insert - key: service_name - from_attribute: service.name - - action: insert - key: loki.resource.labels - value: service_name - -exporters: - debug: - verbosity: detailed - file/traces: - path: /log/otel/traces.log - file/metrics: - path: /log/otel/metrics.log - file/logs: - path: /log/otel/logs.log - otlp: - endpoint: "${JAEGER_ENDPOINT}" - tls: - insecure: true - prometheus: - endpoint: "0.0.0.0:8889" - otlphttp: - endpoint: "http://loki:3100/otlp" - -service: - telemetry: - logs: - level: debug - pipelines: - traces: - receivers: - - otlp - - zipkin - processors: [batch] - exporters: - - debug - - file/traces - - otlp - metrics: - receivers: - - otlp - processors: [batch] - exporters: - - debug - - file/metrics - - prometheus - logs: - receivers: - - otlp - processors: [batch, resource] - exporters: - - debug - - file/logs - - otlphttp - extensions: - - health_check - - zpages \ No newline at end of file diff --git a/compose/prometheus/prometheus.yml b/compose/prometheus/prometheus.yml deleted file mode 100644 index 647cfda1af..0000000000 --- a/compose/prometheus/prometheus.yml +++ /dev/null @@ -1,24 +0,0 @@ -global: - scrape_interval: 10s - -scrape_configs: - - job_name: 'fullstackhero.api' - static_configs: - - targets: ['host.docker.internal:5000'] - - - job_name: otel - static_configs: - - targets: - - 'otel-collector:8889' - - - job_name: otel-collector - static_configs: - - targets: - - 'otel-collector:8888' - - - job_name: 'node-exporter' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - static_configs: - - targets: - - 'node_exporter:9100' \ No newline at end of file diff --git a/src/global.json b/global.json similarity index 58% rename from src/global.json rename to global.json index d5bf446d0c..4b378a34be 100644 --- a/src/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.100-preview.7", "rollForward": "latestFeature" } } \ No newline at end of file diff --git a/src/.dockerignore b/src/.dockerignore deleted file mode 100644 index 3aae53927b..0000000000 --- a/src/.dockerignore +++ /dev/null @@ -1,32 +0,0 @@ -# Include any files or directories that you don't want to be copied to your -# container here (e.g., local build artifacts, temporary files, etc.). -# -# For more help, visit the .dockerignore file reference guide at -# https://docs.docker.com/engine/reference/builder/#dockerignore-file - -**/.DS_Store -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/bin -**/charts -**/docker-compose* -**/compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md diff --git a/src/.editorconfig b/src/.editorconfig index b3fa9a701e..c23f336542 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,17 +1,6 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories root = true -# All files -[*] -indent_style = space - -# Xml files -[*.{xml,csproj,props,targets,ruleset,nuspec,resx}] -indent_size = 2 - -# Json files -[*.{json,config,nswag}] -indent_size = 2 - # C# files [*.cs] @@ -19,115 +8,152 @@ indent_size = 2 # Indentation and spacing indent_size = 4 +indent_style = space tab_width = 4 # New line preferences -end_of_line = lf -insert_final_newline = true +end_of_line = crlf +insert_final_newline = false + +#### .NET Code Actions #### + +# Type members +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Symbol search +dotnet_search_reference_assemblies = true #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true +dotnet_sort_system_directives_first = false file_header_template = unset # this. and Me. preferences -dotnet_style_qualification_for_event = false:silent -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members # Expression-level preferences -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_object_initializer = true:suggestion +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_prefer_auto_properties = true:suggestion -dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true:error dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_return = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true # Field preferences -dotnet_style_readonly_field = true:warning +dotnet_style_readonly_field = true # Parameter preferences -dotnet_code_quality_unused_parameters = all:suggestion +dotnet_code_quality_unused_parameters = all:error # Suppression preferences dotnet_remove_unnecessary_suppression_exclusions = none +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + #### C# Coding Conventions #### -[*.cs] # var preferences -csharp_style_var_elsewhere = false:silent -csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false # Expression-bodied members csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:suggestion -csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_lambdas = true:none +csharp_style_expression_bodied_local_functions = true:error csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_prefer_not_pattern = true:suggestion -csharp_style_prefer_pattern_matching = true:silent -csharp_style_prefer_switch_expression = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true # Null-checking preferences -csharp_style_conditional_delegate_call = true:suggestion +csharp_style_conditional_delegate_call = true # Modifier preferences -csharp_prefer_static_local_function = true:warning -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true:error +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true # Code-block preferences csharp_prefer_braces = true:silent csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent # Expression-level preferences -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true:silent +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true:silent +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences csharp_using_directive_placement = outside_namespace:silent +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + #### C# Formatting Rules #### # New line preferences @@ -174,224 +200,98 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true -csharp_style_namespace_declarations = file_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent -csharp_style_prefer_primary_constructors = true:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -csharp_style_prefer_tuple_swap = true:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -dotnet_diagnostic.CA1032.severity = none -dotnet_diagnostic.CA1812.severity = none -dotnet_diagnostic.S6667.severity = none #### Naming styles #### -[*.{cs,vb}] # Naming rules -dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces -dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion -dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces -dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase - -dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion -dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters -dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase - -dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods -dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties -dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.events_should_be_pascalcase.symbols = events -dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion -dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables -dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase - -dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion -dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants -dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase - -dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion -dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters -dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase - -dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields -dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i -dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion -dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields -dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion -dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields -dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase - -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums -dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions -dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase - -dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case # Symbol specifications -dotnet_naming_symbols.interfaces.applicable_kinds = interface -dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interfaces.required_modifiers = - -dotnet_naming_symbols.enums.applicable_kinds = enum -dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.enums.required_modifiers = - -dotnet_naming_symbols.events.applicable_kinds = event -dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.events.required_modifiers = - -dotnet_naming_symbols.methods.applicable_kinds = method -dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.methods.required_modifiers = +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = -dotnet_naming_symbols.properties.applicable_kinds = property -dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.properties.required_modifiers = - -dotnet_naming_symbols.public_fields.applicable_kinds = field -dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_fields.required_modifiers = - -dotnet_naming_symbols.private_fields.applicable_kinds = field -dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_fields.required_modifiers = - -dotnet_naming_symbols.private_static_fields.applicable_kinds = field -dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_static_fields.required_modifiers = static - -dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum -dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types_and_namespaces.required_modifiers = +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.required_modifiers = -dotnet_naming_symbols.type_parameters.applicable_kinds = namespace -dotnet_naming_symbols.type_parameters.applicable_accessibilities = * -dotnet_naming_symbols.type_parameters.required_modifiers = - -dotnet_naming_symbols.private_constant_fields.applicable_kinds = field -dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_constant_fields.required_modifiers = const - -dotnet_naming_symbols.local_variables.applicable_kinds = local -dotnet_naming_symbols.local_variables.applicable_accessibilities = local -dotnet_naming_symbols.local_variables.required_modifiers = - -dotnet_naming_symbols.local_constants.applicable_kinds = local -dotnet_naming_symbols.local_constants.applicable_accessibilities = local -dotnet_naming_symbols.local_constants.required_modifiers = const +# Naming styles -dotnet_naming_symbols.parameters.applicable_kinds = parameter -dotnet_naming_symbols.parameters.applicable_accessibilities = * -dotnet_naming_symbols.parameters.required_modifiers = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_symbols.public_constant_fields.applicable_kinds = field -dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_constant_fields.required_modifiers = const +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case -dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field -dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static +# Static code analysis rule customizations -dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field -dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = none -dotnet_naming_symbols.local_functions.applicable_kinds = local_function -dotnet_naming_symbols.local_functions.applicable_accessibilities = * -dotnet_naming_symbols.local_functions.required_modifiers = +# CA1515: Consider making public types internal +dotnet_diagnostic.CA1515.severity = none -# Naming styles +# CA1724: Type name conflicts with namespace +dotnet_diagnostic.CA1724.severity = none -dotnet_naming_style.pascalcase.required_prefix = -dotnet_naming_style.pascalcase.required_suffix = -dotnet_naming_style.pascalcase.word_separator = -dotnet_naming_style.pascalcase.capitalization = pascal_case +# S2094: Classes should not be empty +dotnet_diagnostic.S2094.severity = none -dotnet_naming_style.ipascalcase.required_prefix = I -dotnet_naming_style.ipascalcase.required_suffix = -dotnet_naming_style.ipascalcase.word_separator = -dotnet_naming_style.ipascalcase.capitalization = pascal_case +# IDE0058: Expression value is never used +dotnet_diagnostic.IDE0058.severity = none -dotnet_naming_style.tpascalcase.required_prefix = T -dotnet_naming_style.tpascalcase.required_suffix = -dotnet_naming_style.tpascalcase.word_separator = -dotnet_naming_style.tpascalcase.capitalization = pascal_case +# IDE0005: Remove unnecessary usings/imports +dotnet_diagnostic.IDE0005.severity = none -dotnet_naming_style._camelcase.required_prefix = _ -dotnet_naming_style._camelcase.required_suffix = -dotnet_naming_style._camelcase.word_separator = -dotnet_naming_style._camelcase.capitalization = camel_case +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = none -dotnet_naming_style.camelcase.required_prefix = -dotnet_naming_style.camelcase.required_suffix = -dotnet_naming_style.camelcase.word_separator = -dotnet_naming_style.camelcase.capitalization = camel_case +# S125: Remove commented out code +dotnet_diagnostic.S125.severity = none -dotnet_naming_style.s_camelcase.required_prefix = s_ -dotnet_naming_style.s_camelcase.required_suffix = -dotnet_naming_style.s_camelcase.word_separator = -dotnet_naming_style.s_camelcase.capitalization = camel_case +# IDE0053: Use expression body for lambda expression +dotnet_diagnostic.IDE0053.severity = none -dotnet_style_namespace_match_folder = true:suggestion +# IDE0130: Namespace should match project structure +dotnet_diagnostic.IDE0130.severity = none +dotnet_diagnostic.CA1032.severity = none +dotnet_diagnostic.S2326.severity = none -dotnet_diagnostic.CS1591.severity = none -dotnet_diagnostic.CA1724.severity = none -dotnet_diagnostic.CA1305.severity = none -dotnet_diagnostic.CA1040.severity = none -dotnet_diagnostic.CA1848.severity = none -dotnet_diagnostic.CA1034.severity = none +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 indent_size = 4 -end_of_line = lf +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_diagnostic.CA1034.severity = none +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion dotnet_diagnostic.CA1711.severity = none -dotnet_diagnostic.CA1716.severity = none -dotnet_diagnostic.CA1062.severity = none -dotnet_diagnostic.CA1031.severity = none -dotnet_diagnostic.CA1861.severity = none -dotnet_diagnostic.CA2007.severity = none \ No newline at end of file +dotnet_diagnostic.CA1040.severity = none +dotnet_diagnostic.CA1707.severity = none \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj new file mode 100644 index 0000000000..43569d6522 --- /dev/null +++ b/src/Core/Core.csproj @@ -0,0 +1,12 @@ + + + FSH.Framework.Core + FSH.Framework.Core + + + + + + + + \ No newline at end of file diff --git a/src/Core/Domain/Entities/AuditableEntity.cs b/src/Core/Domain/Entities/AuditableEntity.cs new file mode 100644 index 0000000000..e520273c34 --- /dev/null +++ b/src/Core/Domain/Entities/AuditableEntity.cs @@ -0,0 +1,32 @@ +using FSH.Framework.Core.Domain.Interfaces; + +namespace FSH.Framework.Core.Domain.Entities; +public abstract class AuditableEntity : Entity, IAuditable, ISoftDelete +{ + public string? CreatedBy { get; protected set; } + public DateTime CreatedOnUtc { get; protected set; } + public string? LastModifiedBy { get; protected set; } + public DateTime? LastModifiedOnUtc { get; protected set; } + public bool IsDeleted { get; protected set; } + public DateTime? DeletedOnUtc { get; protected set; } + public string? DeletedBy { get; protected set; } + public void SoftDelete(string? by, DateTime whenUtc) + { + if (IsDeleted) return; + IsDeleted = true; + DeletedBy = by; + DeletedOnUtc = whenUtc; + } + public void Restore() + { + if (!IsDeleted) return; + IsDeleted = false; + DeletedBy = null; + DeletedOnUtc = null; + } +} + +public abstract class AuditableEntity : AuditableEntity +{ + protected AuditableEntity() => Id = Guid.NewGuid(); +} \ No newline at end of file diff --git a/src/Core/Domain/Entities/Entity.cs b/src/Core/Domain/Entities/Entity.cs new file mode 100644 index 0000000000..31b71a9904 --- /dev/null +++ b/src/Core/Domain/Entities/Entity.cs @@ -0,0 +1,12 @@ +using FSH.Framework.Core.Domain.Interfaces; + +namespace FSH.Framework.Core.Domain.Entities; + +public abstract class Entity : IHasDomainEvents +{ + public TId Id { get; protected set; } = default!; + private readonly List _domainEvents = new(); + public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly(); + protected void QueueDomainEvent(IDomainEvent @event) => _domainEvents.Add(@event); + public void ClearDomainEvents() => _domainEvents.Clear(); +} \ No newline at end of file diff --git a/src/Core/Domain/Interfaces/IAuditable.cs b/src/Core/Domain/Interfaces/IAuditable.cs new file mode 100644 index 0000000000..c8b0748f2d --- /dev/null +++ b/src/Core/Domain/Interfaces/IAuditable.cs @@ -0,0 +1,8 @@ +namespace FSH.Framework.Core.Domain.Interfaces; +public interface IAuditable +{ + string? CreatedBy { get; } + DateTime CreatedOnUtc { get; } + string? LastModifiedBy { get; } + DateTime? LastModifiedOnUtc { get; } +} \ No newline at end of file diff --git a/src/Core/Domain/Interfaces/IDomainEvent.cs b/src/Core/Domain/Interfaces/IDomainEvent.cs new file mode 100644 index 0000000000..61ea72b346 --- /dev/null +++ b/src/Core/Domain/Interfaces/IDomainEvent.cs @@ -0,0 +1,5 @@ +namespace FSH.Framework.Core.Domain.Interfaces; +public interface IDomainEvent +{ + DateTime OccurredOnUtc { get; } +} \ No newline at end of file diff --git a/src/Core/Domain/Interfaces/IHasDomainEvents.cs b/src/Core/Domain/Interfaces/IHasDomainEvents.cs new file mode 100644 index 0000000000..fae9cec771 --- /dev/null +++ b/src/Core/Domain/Interfaces/IHasDomainEvents.cs @@ -0,0 +1,7 @@ +namespace FSH.Framework.Core.Domain.Interfaces; + +public interface IHasDomainEvents +{ + IReadOnlyCollection DomainEvents { get; } + void ClearDomainEvents(); +} \ No newline at end of file diff --git a/src/Core/Domain/Interfaces/ISoftDelete.cs b/src/Core/Domain/Interfaces/ISoftDelete.cs new file mode 100644 index 0000000000..6ecbe7464c --- /dev/null +++ b/src/Core/Domain/Interfaces/ISoftDelete.cs @@ -0,0 +1,7 @@ +namespace FSH.Framework.Core.Domain.Interfaces; +public interface ISoftDelete +{ + bool IsDeleted { get; } + DateTime? DeletedOnUtc { get; } + string? DeletedBy { get; } +} \ No newline at end of file diff --git a/src/Core/Domain/Interfaces/ITenantOwned.cs b/src/Core/Domain/Interfaces/ITenantOwned.cs new file mode 100644 index 0000000000..672dac31f9 --- /dev/null +++ b/src/Core/Domain/Interfaces/ITenantOwned.cs @@ -0,0 +1,6 @@ +namespace FSH.Framework.Core.Domain.Interfaces; + +public interface ITenantOwned +{ + string? TenantId { get; } +} \ No newline at end of file diff --git a/src/api/framework/Core/Identity/Users/Abstractions/ICurrentUser.cs b/src/Core/Runtime/ICurrentUser.cs similarity index 82% rename from src/api/framework/Core/Identity/Users/Abstractions/ICurrentUser.cs rename to src/Core/Runtime/ICurrentUser.cs index aa5314b007..363df8f40d 100644 --- a/src/api/framework/Core/Identity/Users/Abstractions/ICurrentUser.cs +++ b/src/Core/Runtime/ICurrentUser.cs @@ -1,6 +1,6 @@ using System.Security.Claims; -namespace FSH.Framework.Core.Identity.Users.Abstractions; +namespace FSH.Framework.Core.ExecutionContext; public interface ICurrentUser { string? Name { get; } @@ -16,4 +16,4 @@ public interface ICurrentUser bool IsInRole(string role); IEnumerable? GetUserClaims(); -} +} \ No newline at end of file diff --git a/src/api/framework/Core/Identity/Users/Abstractions/ICurrentUserInitializer.cs b/src/Core/Runtime/ICurrentUserInitializer.cs similarity index 73% rename from src/api/framework/Core/Identity/Users/Abstractions/ICurrentUserInitializer.cs rename to src/Core/Runtime/ICurrentUserInitializer.cs index 2342d75b8d..93bf622e69 100644 --- a/src/api/framework/Core/Identity/Users/Abstractions/ICurrentUserInitializer.cs +++ b/src/Core/Runtime/ICurrentUserInitializer.cs @@ -1,9 +1,9 @@ using System.Security.Claims; -namespace FSH.Framework.Core.Identity.Users.Abstractions; +namespace FSH.Framework.Core.ExecutionContext; public interface ICurrentUserInitializer { void SetCurrentUser(ClaimsPrincipal user); void SetCurrentUserId(string userId); -} +} \ No newline at end of file diff --git a/src/Core/Storage/FileType.cs b/src/Core/Storage/FileType.cs new file mode 100644 index 0000000000..31d82817d7 --- /dev/null +++ b/src/Core/Storage/FileType.cs @@ -0,0 +1,24 @@ +namespace FSH.Framework.Core.Storage; +public enum FileType +{ + Image, + Document, + Pdf +} + +public class FileValidationRules +{ + public IReadOnlyList AllowedExtensions { get; init; } = Array.Empty(); + public int MaxSizeInMB { get; init; } = 5; +} + +public static class FileTypeMetadata +{ + public static FileValidationRules GetRules(FileType type) => + type switch + { + FileType.Image => new() { AllowedExtensions = [".jpg", ".jpeg", ".png"], MaxSizeInMB = 5 }, + FileType.Pdf => new() { AllowedExtensions = [".pdf"], MaxSizeInMB = 10 }, + _ => throw new NotSupportedException($"Unsupported file type: {type}") + }; +} \ No newline at end of file diff --git a/src/Core/Storage/FileUploadRequest.cs b/src/Core/Storage/FileUploadRequest.cs new file mode 100644 index 0000000000..6b7d529013 --- /dev/null +++ b/src/Core/Storage/FileUploadRequest.cs @@ -0,0 +1,7 @@ +namespace FSH.Framework.Core.Storage; +public class FileUploadRequest +{ + public string FileName { get; init; } = default!; + public string ContentType { get; init; } = default!; + public IReadOnlyList Data { get; init; } = Array.Empty(); +} \ No newline at end of file diff --git a/src/Core/Storage/IStorageService.cs b/src/Core/Storage/IStorageService.cs new file mode 100644 index 0000000000..ce166d4c4d --- /dev/null +++ b/src/Core/Storage/IStorageService.cs @@ -0,0 +1,10 @@ +namespace FSH.Framework.Core.Storage; +public interface IStorageService +{ + Task UploadAsync( + FileUploadRequest request, + FileType fileType, + CancellationToken cancellationToken = default) where T : class; + + Task RemoveAsync(string path, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cf4b4bb3de..33780ee01b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,18 +1,48 @@ - - net9.0 - false - false - true - true - enable - enable - true - latest - All - 2.0.4-rc;latest - - - - - \ No newline at end of file + + + net10.0 + + + latest + enable + enable + + + true + true + true + latest + AllEnabledByDefault + + + true + 1591 + + + + 3.0.0-alpha;latest + + + true + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + Mukesh Murugan + FullStackHero + 3.0.0 + https://github.com/fullstackhero/dotnet-starter-kit + FSH;Modular;CQRS;VerticalSlice + + true + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 7015fadbab..a9a9673d40 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -9,88 +9,13 @@ true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + \ No newline at end of file diff --git a/src/Dockerfile.Blazor b/src/Dockerfile.Blazor deleted file mode 100644 index 2438ffea64..0000000000 --- a/src/Dockerfile.Blazor +++ /dev/null @@ -1,13 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env -WORKDIR /app - -COPY . ./ -RUN dotnet publish ./apps/blazor/client/Client.csproj -c Release -o output - -FROM nginx:alpine -WORKDIR /usr/share/nginx/html -COPY --from=build-env /app/output/wwwroot . - -COPY ./apps/blazor/nginx.conf /etc/nginx/nginx.conf - -EXPOSE 80 \ No newline at end of file diff --git a/src/FSH.Framework.slnx b/src/FSH.Framework.slnx new file mode 100644 index 0000000000..bd888462c7 --- /dev/null +++ b/src/FSH.Framework.slnx @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/FSH.Starter.sln b/src/FSH.Starter.sln deleted file mode 100644 index 904c59f770..0000000000 --- a/src/FSH.Starter.sln +++ /dev/null @@ -1,287 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{F3DF5AC5-8CDC-46D4-969D-1245A6880215}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A32CEFB3-4E50-401E-8835-787534414F41}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Catalog", "Catalog", "{93324D12-DE1B-4C1B-934A-92AA140FF6F6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Todo", "Todo", "{79981A5A-207A-4A16-A21B-5E80394082F6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Framework", "_Framework", "{05248A38-0F34-4E59-A3D1-B07097987AFB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Migrations", "Migrations", "{12F8343D-20A6-4E24-B0F5-3A66F2228CF6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebApi", "WebApi", "{CE64E92B-E088-46FB-9028-7FB6B67DEC55}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{2B1F75CE-07A6-4C19-A2E3-F9E062CFDDFB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "api\framework\Infrastructure\Infrastructure.csproj", "{294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "api\framework\Core\Core.csproj", "{A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "api\server\Server.csproj", "{86BD3DF6-A3E9-4839-8036-813A20DC8AD6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSSQL", "api\migrations\MSSQL\MSSQL.csproj", "{ECCEA352-8953-49D6-8F87-8AB361499420}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PostgreSQL", "api\migrations\PostgreSQL\PostgreSQL.csproj", "{D64AD07C-A711-42D8-8653-EDCD7A825A44}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo", "api\modules\Todo\Todo.csproj", "{B3866EEF-8F46-4302-ABAC-A95EE2F27331}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.Application", "api\modules\Catalog\Catalog.Application\Catalog.Application.csproj", "{8C7DAF8E-F792-4092-8BBF-31A6B898B39A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.Domain", "api\modules\Catalog\Catalog.Domain\Catalog.Domain.csproj", "{B15705B5-041C-4F1E-8342-AD03182EDD42}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.Infrastructure", "api\modules\Catalog\Catalog.Infrastructure\Catalog.Infrastructure.csproj", "{89FE1C3B-29D3-48A8-8E7D-90C261D266C5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "apps\blazor\client\Client.csproj", "{BCE4A428-8B97-4B56-AE45-496EE3906667}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "apps\blazor\infrastructure\Infrastructure.csproj", "{27BEF279-AE73-43DC-92A9-FD7021A999D0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "apps\blazor\shared\Shared.csproj", "{34359707-CE66-4DF0-9EF4-D7544B615564}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspire", "Aspire", "{D36E77BC-4568-4BC8-9506-1EFB7B1CD335}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceDefaults", "aspire\service-defaults\ServiceDefaults.csproj", "{990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "aspire\host\Host.csproj", "{2119CE89-308D-4932-BFCE-8CDC0A05EB9E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{FE1B1E84-F993-4840-9CAB-9082EB523FDD}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{F17769D7-0E41-4E80-BDD4-282EBE7B5199}" - ProjectSection(SolutionItems) = preProject - GetToken.http = GetToken.http - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|x64.ActiveCfg = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|x64.Build.0 = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|x86.ActiveCfg = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Debug|x86.Build.0 = Debug|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|Any CPU.Build.0 = Release|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|x64.ActiveCfg = Release|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|x64.Build.0 = Release|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|x86.ActiveCfg = Release|Any CPU - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB}.Release|x86.Build.0 = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|x64.ActiveCfg = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|x64.Build.0 = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|x86.ActiveCfg = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Debug|x86.Build.0 = Debug|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|Any CPU.Build.0 = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|x64.ActiveCfg = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|x64.Build.0 = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|x86.ActiveCfg = Release|Any CPU - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31}.Release|x86.Build.0 = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|x64.ActiveCfg = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|x64.Build.0 = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|x86.ActiveCfg = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Debug|x86.Build.0 = Debug|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|Any CPU.Build.0 = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|x64.ActiveCfg = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|x64.Build.0 = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|x86.ActiveCfg = Release|Any CPU - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6}.Release|x86.Build.0 = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|x64.ActiveCfg = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|x64.Build.0 = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|x86.ActiveCfg = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Debug|x86.Build.0 = Debug|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|Any CPU.Build.0 = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|x64.ActiveCfg = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|x64.Build.0 = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|x86.ActiveCfg = Release|Any CPU - {ECCEA352-8953-49D6-8F87-8AB361499420}.Release|x86.Build.0 = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|x64.ActiveCfg = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|x64.Build.0 = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|x86.ActiveCfg = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Debug|x86.Build.0 = Debug|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|Any CPU.Build.0 = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|x64.ActiveCfg = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|x64.Build.0 = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|x86.ActiveCfg = Release|Any CPU - {D64AD07C-A711-42D8-8653-EDCD7A825A44}.Release|x86.Build.0 = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|x64.ActiveCfg = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|x64.Build.0 = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|x86.ActiveCfg = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Debug|x86.Build.0 = Debug|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|Any CPU.Build.0 = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|x64.ActiveCfg = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|x64.Build.0 = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|x86.ActiveCfg = Release|Any CPU - {B3866EEF-8F46-4302-ABAC-A95EE2F27331}.Release|x86.Build.0 = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|x64.ActiveCfg = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|x64.Build.0 = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|x86.ActiveCfg = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Debug|x86.Build.0 = Debug|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|Any CPU.Build.0 = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|x64.ActiveCfg = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|x64.Build.0 = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|x86.ActiveCfg = Release|Any CPU - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A}.Release|x86.Build.0 = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|x64.ActiveCfg = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|x64.Build.0 = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|x86.ActiveCfg = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Debug|x86.Build.0 = Debug|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|Any CPU.Build.0 = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|x64.ActiveCfg = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|x64.Build.0 = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|x86.ActiveCfg = Release|Any CPU - {B15705B5-041C-4F1E-8342-AD03182EDD42}.Release|x86.Build.0 = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|x64.ActiveCfg = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|x64.Build.0 = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|x86.ActiveCfg = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Debug|x86.Build.0 = Debug|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|Any CPU.Build.0 = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|x64.ActiveCfg = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|x64.Build.0 = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|x86.ActiveCfg = Release|Any CPU - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5}.Release|x86.Build.0 = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|x64.ActiveCfg = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|x64.Build.0 = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|x86.ActiveCfg = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Debug|x86.Build.0 = Debug|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|Any CPU.Build.0 = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|x64.ActiveCfg = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|x64.Build.0 = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|x86.ActiveCfg = Release|Any CPU - {BCE4A428-8B97-4B56-AE45-496EE3906667}.Release|x86.Build.0 = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|x64.ActiveCfg = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|x64.Build.0 = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|x86.ActiveCfg = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Debug|x86.Build.0 = Debug|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|Any CPU.Build.0 = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|x64.ActiveCfg = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|x64.Build.0 = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|x86.ActiveCfg = Release|Any CPU - {27BEF279-AE73-43DC-92A9-FD7021A999D0}.Release|x86.Build.0 = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|Any CPU.Build.0 = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|x64.ActiveCfg = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|x64.Build.0 = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|x86.ActiveCfg = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Debug|x86.Build.0 = Debug|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|Any CPU.ActiveCfg = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|Any CPU.Build.0 = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|x64.ActiveCfg = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|x64.Build.0 = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|x86.ActiveCfg = Release|Any CPU - {34359707-CE66-4DF0-9EF4-D7544B615564}.Release|x86.Build.0 = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|x64.ActiveCfg = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|x64.Build.0 = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|x86.ActiveCfg = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Debug|x86.Build.0 = Debug|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|Any CPU.Build.0 = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|x64.ActiveCfg = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|x64.Build.0 = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|x86.ActiveCfg = Release|Any CPU - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6}.Release|x86.Build.0 = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|x64.ActiveCfg = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|x64.Build.0 = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|x86.ActiveCfg = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Debug|x86.Build.0 = Debug|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|Any CPU.Build.0 = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|x64.ActiveCfg = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|x64.Build.0 = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|x86.ActiveCfg = Release|Any CPU - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E}.Release|x86.Build.0 = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|x64.ActiveCfg = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|x64.Build.0 = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|x86.ActiveCfg = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Debug|x86.Build.0 = Debug|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|Any CPU.Build.0 = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|x64.ActiveCfg = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|x64.Build.0 = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|x86.ActiveCfg = Release|Any CPU - {49AA63BF-3DBA-4490-9470-5AE0EB7F49F0}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {F3DF5AC5-8CDC-46D4-969D-1245A6880215} = {CE64E92B-E088-46FB-9028-7FB6B67DEC55} - {93324D12-DE1B-4C1B-934A-92AA140FF6F6} = {F3DF5AC5-8CDC-46D4-969D-1245A6880215} - {79981A5A-207A-4A16-A21B-5E80394082F6} = {F3DF5AC5-8CDC-46D4-969D-1245A6880215} - {05248A38-0F34-4E59-A3D1-B07097987AFB} = {CE64E92B-E088-46FB-9028-7FB6B67DEC55} - {12F8343D-20A6-4E24-B0F5-3A66F2228CF6} = {CE64E92B-E088-46FB-9028-7FB6B67DEC55} - {294D6FF8-9379-4AB8-A203-8D0CC85FBFBB} = {05248A38-0F34-4E59-A3D1-B07097987AFB} - {A1D828E4-6B83-4BA2-B8E9-B21CE3BE8A31} = {05248A38-0F34-4E59-A3D1-B07097987AFB} - {86BD3DF6-A3E9-4839-8036-813A20DC8AD6} = {CE64E92B-E088-46FB-9028-7FB6B67DEC55} - {ECCEA352-8953-49D6-8F87-8AB361499420} = {12F8343D-20A6-4E24-B0F5-3A66F2228CF6} - {D64AD07C-A711-42D8-8653-EDCD7A825A44} = {12F8343D-20A6-4E24-B0F5-3A66F2228CF6} - {B3866EEF-8F46-4302-ABAC-A95EE2F27331} = {79981A5A-207A-4A16-A21B-5E80394082F6} - {8C7DAF8E-F792-4092-8BBF-31A6B898B39A} = {93324D12-DE1B-4C1B-934A-92AA140FF6F6} - {B15705B5-041C-4F1E-8342-AD03182EDD42} = {93324D12-DE1B-4C1B-934A-92AA140FF6F6} - {89FE1C3B-29D3-48A8-8E7D-90C261D266C5} = {93324D12-DE1B-4C1B-934A-92AA140FF6F6} - {BCE4A428-8B97-4B56-AE45-496EE3906667} = {2B1F75CE-07A6-4C19-A2E3-F9E062CFDDFB} - {27BEF279-AE73-43DC-92A9-FD7021A999D0} = {2B1F75CE-07A6-4C19-A2E3-F9E062CFDDFB} - {34359707-CE66-4DF0-9EF4-D7544B615564} = {2B1F75CE-07A6-4C19-A2E3-F9E062CFDDFB} - {990CA37A-86D3-4FE6-B777-3CF0DDAC6BF6} = {D36E77BC-4568-4BC8-9506-1EFB7B1CD335} - {2119CE89-308D-4932-BFCE-8CDC0A05EB9E} = {D36E77BC-4568-4BC8-9506-1EFB7B1CD335} - {FE1B1E84-F993-4840-9CAB-9082EB523FDD} = {CE64E92B-E088-46FB-9028-7FB6B67DEC55} - {F17769D7-0E41-4E80-BDD4-282EBE7B5199} = {FE1B1E84-F993-4840-9CAB-9082EB523FDD} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {EA8248C2-3877-4AF7-8777-A17E7881E030} - EndGlobalSection -EndGlobal diff --git a/src/GetToken.http b/src/GetToken.http deleted file mode 100644 index 0de6481c71..0000000000 --- a/src/GetToken.http +++ /dev/null @@ -1,10 +0,0 @@ -@Host = https://localhost:7000 - -POST {{Host}}/api/token/ -Accept: application/json -Content-Type: application/json -tenant: root -{ - "email":"admin@root.com", - "password":"123Pa$$word!" -} diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000000..3da9a12c1b --- /dev/null +++ b/src/Infrastructure/Infrastructure.csproj @@ -0,0 +1,21 @@ + + + FSH.Framework.Infrastructure + FSH.Framework.Infrastructure + + + + + + + + + + + + + + + + + diff --git a/src/Infrastructure/Mediator/Extensions.cs b/src/Infrastructure/Mediator/Extensions.cs new file mode 100644 index 0000000000..65bb8343eb --- /dev/null +++ b/src/Infrastructure/Mediator/Extensions.cs @@ -0,0 +1,31 @@ +using FSH.Framework.Infrastructure.Mediator.Validation; +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace FSH.Framework.Infrastructure.Mediator; +public static class Extensions +{ + public static IServiceCollection AddMediator(this IServiceCollection services, params Assembly[] assemblies) + { + ArgumentNullException.ThrowIfNull(services); + + services.AddScoped(); + RegisterHandlers(services, assemblies); + services.Decorate(); + + return services; + } + + internal static void RegisterHandlers(IServiceCollection services, Assembly[] assemblies) + { + // Deduplicate Assemblies + var distinctAssemblies = assemblies.Distinct().ToArray(); + + // Scan for handlers in provided assemblies + services.Scan(scan => scan + .FromAssemblies(distinctAssemblies) + .AddClasses(c => c.AssignableTo(typeof(IRequestHandler<,>))) + .AsImplementedInterfaces() + .WithScopedLifetime()); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Mediator/IMediator.cs b/src/Infrastructure/Mediator/IMediator.cs new file mode 100644 index 0000000000..83c05fd8f8 --- /dev/null +++ b/src/Infrastructure/Mediator/IMediator.cs @@ -0,0 +1,7 @@ +namespace FSH.Framework.Infrastructure.Mediator; + +public interface IMediator +{ + Task SendAsync(IRequest request, CancellationToken cancellationToken = default); + Task SendAsync(IRequest request, CancellationToken cancellationToken = default); +} diff --git a/src/Infrastructure/Mediator/IRequest.cs b/src/Infrastructure/Mediator/IRequest.cs new file mode 100644 index 0000000000..54dc1106a8 --- /dev/null +++ b/src/Infrastructure/Mediator/IRequest.cs @@ -0,0 +1,9 @@ +namespace FSH.Framework.Infrastructure.Mediator; + +public interface IRequest +{ +} + +public interface IRequest +{ +} \ No newline at end of file diff --git a/src/Infrastructure/Mediator/IRequestHandler.cs b/src/Infrastructure/Mediator/IRequestHandler.cs new file mode 100644 index 0000000000..d77c56e5f2 --- /dev/null +++ b/src/Infrastructure/Mediator/IRequestHandler.cs @@ -0,0 +1,10 @@ +namespace FSH.Framework.Infrastructure.Mediator; +public interface IRequestHandler where TRequest : IRequest +{ + Task HandleAsync(TRequest request, CancellationToken cancellationToken); +} + +public interface IRequestHandler where TRequest : IRequest +{ + Task HandleAsync(TRequest request, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Infrastructure/Mediator/Mediator.cs b/src/Infrastructure/Mediator/Mediator.cs new file mode 100644 index 0000000000..fc3b7679e1 --- /dev/null +++ b/src/Infrastructure/Mediator/Mediator.cs @@ -0,0 +1,56 @@ +namespace FSH.Framework.Infrastructure.Mediator; + +public class Mediator : IMediator +{ + private readonly IServiceProvider _serviceProvider; + + public Mediator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) + { + var requestType = request.GetType(); + var responseType = typeof(TResponse); + var handlerType = typeof(IRequestHandler<,>).MakeGenericType(requestType, responseType); + + var handler = _serviceProvider.GetService(handlerType); + if (handler == null) + { + throw new InvalidOperationException($"No handler registered for {requestType.Name}"); + } + + var method = handlerType.GetMethod(nameof(IRequestHandler, TResponse>.HandleAsync)); + if (method == null) + { + throw new InvalidOperationException($"HandleAsync method not found on {handlerType.Name}"); + } + + var result = await (Task)method.Invoke(handler, new object[] { request, cancellationToken })!; + return result; + } + + public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) + { + var requestType = request.GetType(); + var handlerType = typeof(IRequestHandler<>).MakeGenericType(requestType); + + var handler = _serviceProvider.GetService(handlerType); + if (handler == null) + { + throw new InvalidOperationException($"No handler registered for {requestType.Name}"); + } + + var method = handlerType.GetMethod(nameof(IRequestHandler.HandleAsync)); + if (method == null) + { + throw new InvalidOperationException($"HandleAsync method not found on {handlerType.Name}"); + } + + await (Task)method.Invoke(handler, new object[] { request, cancellationToken })!; + } +} + + + diff --git a/src/Infrastructure/Mediator/Validation/RequestValidation.cs b/src/Infrastructure/Mediator/Validation/RequestValidation.cs new file mode 100644 index 0000000000..f1be86e0bc --- /dev/null +++ b/src/Infrastructure/Mediator/Validation/RequestValidation.cs @@ -0,0 +1,25 @@ +namespace FSH.Framework.Infrastructure.Mediator.Validation; + +public sealed class RequestValidation : IMediator +{ + private readonly IMediator _inner; + private readonly IServiceProvider _serviceProvider; + + public RequestValidation(IMediator inner, IServiceProvider serviceProvider) + { + _inner = inner; + _serviceProvider = serviceProvider; + } + + public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) + { + await ValidationHelper.ValidateAsync(request, _serviceProvider, cancellationToken); + return await _inner.SendAsync(request, cancellationToken); + } + + public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) + { + await ValidationHelper.ValidateAsync(request, _serviceProvider, cancellationToken); + await _inner.SendAsync(request, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Mediator/Validation/ValidationHelper.cs b/src/Infrastructure/Mediator/Validation/ValidationHelper.cs new file mode 100644 index 0000000000..2b89c89f54 --- /dev/null +++ b/src/Infrastructure/Mediator/Validation/ValidationHelper.cs @@ -0,0 +1,38 @@ +using FluentValidation; +using Microsoft.Extensions.DependencyInjection; + +namespace FSH.Framework.Infrastructure.Mediator.Validation; +internal static class ValidationHelper +{ + public static async Task ValidateAsync(T request, IServiceProvider provider, CancellationToken ct = default) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(provider); + + var requestType = request.GetType(); + var validatorType = typeof(IValidator<>).MakeGenericType(requestType); + var validators = provider.GetServices(validatorType).Cast().ToList(); + + if (validators.Count == 0) return; + + var contextType = typeof(ValidationContext<>).MakeGenericType(requestType); + var context = Activator.CreateInstance(contextType, request)!; + + var failures = new List(); + + foreach (var validator in validators) + { + var validateAsyncMethod = validator.GetType() + .GetMethod("ValidateAsync", new[] { contextType, typeof(CancellationToken) })!; + + var task = (Task) + validateAsyncMethod.Invoke(validator, new[] { context, ct })!; + + var result = await task; + failures.AddRange(result.Errors.Where(f => f != null)); + } + + if (failures.Count > 0) + throw new ValidationException(failures); + } +} \ No newline at end of file diff --git a/src/Shared/Authorization/AppConstants.cs b/src/Shared/Authorization/AppConstants.cs deleted file mode 100644 index 5334ee902f..0000000000 --- a/src/Shared/Authorization/AppConstants.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.ObjectModel; - -namespace FSH.Starter.Shared.Authorization; -public static class AppConstants -{ - public static readonly Collection SupportedImageFormats = - [ - ".jpeg", - ".jpg", - ".png" - ]; - public static readonly string StandardImageFormat = "image/jpeg"; - public static readonly int MaxImageWidth = 1500; - public static readonly int MaxImageHeight = 1500; - public static readonly long MaxAllowedSize = 1000000; // Allows Max File Size of 1 Mb. -} diff --git a/src/Shared/Authorization/ClaimsPrincipalExtensions.cs b/src/Shared/Authorization/ClaimsPrincipalExtensions.cs deleted file mode 100644 index 4c4398d73a..0000000000 --- a/src/Shared/Authorization/ClaimsPrincipalExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Security.Claims; - -namespace FSH.Starter.Shared.Authorization; -public static class ClaimsPrincipalExtensions -{ - public static string? GetEmail(this ClaimsPrincipal principal) - => principal.FindFirstValue(ClaimTypes.Email); - - public static string? GetTenant(this ClaimsPrincipal principal) - => principal.FindFirstValue(FshClaims.Tenant); - - public static string? GetFullName(this ClaimsPrincipal principal) - => principal?.FindFirst(FshClaims.Fullname)?.Value; - - public static string? GetFirstName(this ClaimsPrincipal principal) - => principal?.FindFirst(ClaimTypes.Name)?.Value; - - public static string? GetSurname(this ClaimsPrincipal principal) - => principal?.FindFirst(ClaimTypes.Surname)?.Value; - - public static string? GetPhoneNumber(this ClaimsPrincipal principal) - => principal.FindFirstValue(ClaimTypes.MobilePhone); - - public static string? GetUserId(this ClaimsPrincipal principal) - => principal.FindFirstValue(ClaimTypes.NameIdentifier); - - public static Uri? GetImageUrl(this ClaimsPrincipal principal) - { - var imageUrl = principal.FindFirstValue(FshClaims.ImageUrl); - return Uri.TryCreate(imageUrl, UriKind.Absolute, out var uri) ? uri : null; - } - - public static DateTimeOffset GetExpiration(this ClaimsPrincipal principal) => - DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64( - principal.FindFirstValue(FshClaims.Expiration))); - - private static string? FindFirstValue(this ClaimsPrincipal principal, string claimType) => - principal is null - ? throw new ArgumentNullException(nameof(principal)) - : principal.FindFirst(claimType)?.Value; -} diff --git a/src/Shared/Authorization/FshActions.cs b/src/Shared/Authorization/FshActions.cs deleted file mode 100644 index a29f17e51e..0000000000 --- a/src/Shared/Authorization/FshActions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FSH.Starter.Shared.Authorization; -public static class FshActions -{ - public const string View = nameof(View); - public const string Search = nameof(Search); - public const string Create = nameof(Create); - public const string Update = nameof(Update); - public const string Delete = nameof(Delete); - public const string Export = nameof(Export); - public const string Generate = nameof(Generate); - public const string Clean = nameof(Clean); - public const string UpgradeSubscription = nameof(UpgradeSubscription); -} diff --git a/src/Shared/Authorization/FshClaims.cs b/src/Shared/Authorization/FshClaims.cs deleted file mode 100644 index bdf9020684..0000000000 --- a/src/Shared/Authorization/FshClaims.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace FSH.Starter.Shared.Authorization; - -public static class FshClaims -{ - public const string Tenant = "tenant"; - public const string Fullname = "fullName"; - public const string Permission = "permission"; - public const string ImageUrl = "image_url"; - public const string IpAddress = "ipAddress"; - public const string Expiration = "exp"; -} diff --git a/src/Shared/Authorization/FshPermissions.cs b/src/Shared/Authorization/FshPermissions.cs deleted file mode 100644 index fad6676ee8..0000000000 --- a/src/Shared/Authorization/FshPermissions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.ObjectModel; - -namespace FSH.Starter.Shared.Authorization; - -public static class FshPermissions -{ - private static readonly FshPermission[] AllPermissions = - [ - //tenants - new("View Tenants", FshActions.View, FshResources.Tenants, IsRoot: true), - new("Create Tenants", FshActions.Create, FshResources.Tenants, IsRoot: true), - new("Update Tenants", FshActions.Update, FshResources.Tenants, IsRoot: true), - new("Upgrade Tenant Subscription", FshActions.UpgradeSubscription, FshResources.Tenants, IsRoot: true), - - //identity - new("View Users", FshActions.View, FshResources.Users), - new("Search Users", FshActions.Search, FshResources.Users), - new("Create Users", FshActions.Create, FshResources.Users), - new("Update Users", FshActions.Update, FshResources.Users), - new("Delete Users", FshActions.Delete, FshResources.Users), - new("Export Users", FshActions.Export, FshResources.Users), - new("View UserRoles", FshActions.View, FshResources.UserRoles), - new("Update UserRoles", FshActions.Update, FshResources.UserRoles), - new("View Roles", FshActions.View, FshResources.Roles), - new("Create Roles", FshActions.Create, FshResources.Roles), - new("Update Roles", FshActions.Update, FshResources.Roles), - new("Delete Roles", FshActions.Delete, FshResources.Roles), - new("View RoleClaims", FshActions.View, FshResources.RoleClaims), - new("Update RoleClaims", FshActions.Update, FshResources.RoleClaims), - - //products - new("View Products", FshActions.View, FshResources.Products, IsBasic: true), - new("Search Products", FshActions.Search, FshResources.Products, IsBasic: true), - new("Create Products", FshActions.Create, FshResources.Products), - new("Update Products", FshActions.Update, FshResources.Products), - new("Delete Products", FshActions.Delete, FshResources.Products), - new("Export Products", FshActions.Export, FshResources.Products), - - //brands - new("View Brands", FshActions.View, FshResources.Brands, IsBasic: true), - new("Search Brands", FshActions.Search, FshResources.Brands, IsBasic: true), - new("Create Brands", FshActions.Create, FshResources.Brands), - new("Update Brands", FshActions.Update, FshResources.Brands), - new("Delete Brands", FshActions.Delete, FshResources.Brands), - new("Export Brands", FshActions.Export, FshResources.Brands), - - //todos - new("View Todos", FshActions.View, FshResources.Todos, IsBasic: true), - new("Search Todos", FshActions.Search, FshResources.Todos, IsBasic: true), - new("Create Todos", FshActions.Create, FshResources.Todos), - new("Update Todos", FshActions.Update, FshResources.Todos), - new("Delete Todos", FshActions.Delete, FshResources.Todos), - new("Export Todos", FshActions.Export, FshResources.Todos), - - new("View Hangfire", FshActions.View, FshResources.Hangfire), - new("View Dashboard", FshActions.View, FshResources.Dashboard), - - //audit - new("View Audit Trails", FshActions.View, FshResources.AuditTrails), - ]; - - public static IReadOnlyList All { get; } = new ReadOnlyCollection(AllPermissions); - public static IReadOnlyList Root { get; } = new ReadOnlyCollection(AllPermissions.Where(p => p.IsRoot).ToArray()); - public static IReadOnlyList Admin { get; } = new ReadOnlyCollection(AllPermissions.Where(p => !p.IsRoot).ToArray()); - public static IReadOnlyList Basic { get; } = new ReadOnlyCollection(AllPermissions.Where(p => p.IsBasic).ToArray()); -} - -public record FshPermission(string Description, string Action, string Resource, bool IsBasic = false, bool IsRoot = false) -{ - public string Name => NameFor(Action, Resource); - public static string NameFor(string action, string resource) - { - return $"Permissions.{resource}.{action}"; - } -} - - diff --git a/src/Shared/Authorization/FshResources.cs b/src/Shared/Authorization/FshResources.cs deleted file mode 100644 index e8d276c470..0000000000 --- a/src/Shared/Authorization/FshResources.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FSH.Starter.Shared.Authorization; -public static class FshResources -{ - public const string Tenants = nameof(Tenants); - public const string Dashboard = nameof(Dashboard); - public const string Hangfire = nameof(Hangfire); - public const string Users = nameof(Users); - public const string UserRoles = nameof(UserRoles); - public const string Roles = nameof(Roles); - public const string RoleClaims = nameof(RoleClaims); - public const string Products = nameof(Products); - public const string Brands = nameof(Brands); - public const string Todos = nameof(Todos); - public const string AuditTrails = nameof(AuditTrails); -} diff --git a/src/Shared/Authorization/FshRoles.cs b/src/Shared/Authorization/FshRoles.cs deleted file mode 100644 index e471e50930..0000000000 --- a/src/Shared/Authorization/FshRoles.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.ObjectModel; - -namespace FSH.Starter.Shared.Authorization; - -public static class FshRoles -{ - public const string Admin = nameof(Admin); - public const string Basic = nameof(Basic); - - public static IReadOnlyList DefaultRoles { get; } = new ReadOnlyCollection(new[] - { - Admin, - Basic - }); - - public static bool IsDefault(string roleName) => DefaultRoles.Any(r => r == roleName); -} diff --git a/src/Shared/Authorization/IdentityConstants.cs b/src/Shared/Authorization/IdentityConstants.cs deleted file mode 100644 index 7d15a098be..0000000000 --- a/src/Shared/Authorization/IdentityConstants.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Starter.Shared.Authorization; -public static class IdentityConstants -{ - public const int PasswordLength = 6; - public const string SchemaName = "identity"; -} diff --git a/src/Shared/Authorization/TenantConstants.cs b/src/Shared/Authorization/TenantConstants.cs deleted file mode 100644 index 984fe77e1a..0000000000 --- a/src/Shared/Authorization/TenantConstants.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FSH.Starter.Shared.Authorization; -public static class TenantConstants -{ - public static class Root - { - public const string Id = "root"; - public const string Name = "Root"; - public const string EmailAddress = "admin@root.com"; - public const string DefaultProfilePicture = "assets/defaults/profile-picture.webp"; - } - - public const string DefaultPassword = "123Pa$$word!"; - - public const string Identifier = "tenant"; - -} diff --git a/src/Shared/Constants/SchemaNames.cs b/src/Shared/Constants/SchemaNames.cs deleted file mode 100644 index 6f6763a8b2..0000000000 --- a/src/Shared/Constants/SchemaNames.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Shared.Constants; -public static class SchemaNames -{ - public const string Todo = "todo"; - public const string Catalog = "catalog"; - public const string Tenant = "tenant"; -} diff --git a/src/Shared/Shared.csproj b/src/Shared/Shared.csproj index 125f4c93bc..33fe52ad7e 100644 --- a/src/Shared/Shared.csproj +++ b/src/Shared/Shared.csproj @@ -1,9 +1,6 @@  - - - net9.0 - enable - enable - - + + FSH.Framework.Shared + FSH.Framework.Shared + diff --git a/src/Web/Endpoints/Abstractions/IEndpointDefinition.cs b/src/Web/Endpoints/Abstractions/IEndpointDefinition.cs new file mode 100644 index 0000000000..ef7871ae5b --- /dev/null +++ b/src/Web/Endpoints/Abstractions/IEndpointDefinition.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Builder; + +namespace FSH.Framework.Web.Endpoints.Abstractions; + +public interface IEndpointDefinition +{ + void DefineEndpoints(WebApplication app); +} \ No newline at end of file diff --git a/src/Web/Endpoints/Extensions.cs b/src/Web/Endpoints/Extensions.cs new file mode 100644 index 0000000000..d6571d7870 --- /dev/null +++ b/src/Web/Endpoints/Extensions.cs @@ -0,0 +1,25 @@ +using FSH.Framework.Web.Endpoints.Abstractions; +using Microsoft.AspNetCore.Builder; +using System.Reflection; + +namespace FSH.Framework.Web.Endpoints; +public static class Extensions +{ + public static WebApplication MapEndpointsFromAssembly(this WebApplication app, Assembly assembly) + { + var endpointDefinitions = assembly + .GetTypes() + .Where(t => t.IsAssignableTo(typeof(IEndpointDefinition)) + && !t.IsAbstract + && !t.IsInterface) + .Select(Activator.CreateInstance) + .Cast(); + + foreach (var endpointDefinition in endpointDefinitions) + { + endpointDefinition.DefineEndpoints(app); + } + + return app; + } +} \ No newline at end of file diff --git a/src/Web/Observability/Logging/Enrichers/TenantIdEnricher.cs b/src/Web/Observability/Logging/Enrichers/TenantIdEnricher.cs new file mode 100644 index 0000000000..2eb7225ce7 --- /dev/null +++ b/src/Web/Observability/Logging/Enrichers/TenantIdEnricher.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Http; +using Serilog.Core; +using Serilog.Events; +using System.Security.Claims; + +namespace FSH.Framework.Web.Observability.Logging.Enrichers; + +public sealed class TenantIdEnricher : ILogEventEnricher +{ + public const string PropertyName = "TenantId"; + private static readonly HttpContextAccessor Accessor = new(); + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + ArgumentNullException.ThrowIfNull(logEvent); + ArgumentNullException.ThrowIfNull(propertyFactory); + + var http = Accessor.HttpContext; + string tenantId = + GetFromClaims(http?.User) ?? + GetFromItems(http) ?? + GetFromHeader(http) ?? + "unknown"; + + var prop = propertyFactory.CreateProperty(PropertyName, tenantId, false); + logEvent.AddPropertyIfAbsent(prop); + } + + private static string? GetFromClaims(ClaimsPrincipal? user) => + user?.FindFirstValue("tenant_id") + ?? user?.FindFirstValue("tid") + ?? user?.FindFirstValue("TenantId"); + + private static string? GetFromItems(HttpContext? ctx) => + ctx is not null && ctx.Items.TryGetValue("TenantId", out var v) ? v?.ToString() : null; + + private static string? GetFromHeader(HttpContext? ctx) => + ctx?.Request?.Headers.TryGetValue("X-Tenant", out var val) == true ? val.ToString() : null; +} \ No newline at end of file diff --git a/src/Web/Observability/Logging/SerilogExtensions.cs b/src/Web/Observability/Logging/SerilogExtensions.cs new file mode 100644 index 0000000000..4e5f034098 --- /dev/null +++ b/src/Web/Observability/Logging/SerilogExtensions.cs @@ -0,0 +1,28 @@ +using FSH.Framework.Web.Observability.Logging.Enrichers; +using Microsoft.AspNetCore.Builder; +using Serilog; +using Serilog.Events; +using Serilog.Filters; + +namespace FSH.Framework.Web.Observability.Logging; +public static class SerilogExtensions +{ + public static WebApplicationBuilder UseStructuredLogging(this WebApplicationBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + builder.Host.UseSerilog((context, logger) => + { + logger.ReadFrom.Configuration(context.Configuration); + logger.Enrich.FromLogContext(); + logger.Enrich.With(new TenantIdEnricher()); + logger + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Error) + .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) + .MinimumLevel.Override("Finbuckle.MultiTenant", LogEventLevel.Warning) + .Filter.ByExcluding(Matching.FromSource("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware")); + }); + return builder; + } +} \ No newline at end of file diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj new file mode 100644 index 0000000000..cb47d1d60e --- /dev/null +++ b/src/Web/Web.csproj @@ -0,0 +1,18 @@ + + + FSH.Framework.Web + FSH.Framework.Web + + + + + + + + + + + + + + diff --git a/src/api/framework/Core/Audit/AuditTrail.cs b/src/api/framework/Core/Audit/AuditTrail.cs deleted file mode 100644 index 97448ac39f..0000000000 --- a/src/api/framework/Core/Audit/AuditTrail.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FSH.Framework.Core.Audit; -public class AuditTrail -{ - public Guid Id { get; set; } - public Guid UserId { get; set; } - public string? Operation { get; set; } - public string? Entity { get; set; } - public DateTimeOffset DateTime { get; set; } - public string? PreviousValues { get; set; } - public string? NewValues { get; set; } - public string? ModifiedProperties { get; set; } - public string? PrimaryKey { get; set; } -} diff --git a/src/api/framework/Core/Audit/IAuditService.cs b/src/api/framework/Core/Audit/IAuditService.cs deleted file mode 100644 index 9c62f4d0db..0000000000 --- a/src/api/framework/Core/Audit/IAuditService.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Core.Audit; -public interface IAuditService -{ - Task> GetUserTrailsAsync(Guid userId); -} diff --git a/src/api/framework/Core/Audit/TrailDto.cs b/src/api/framework/Core/Audit/TrailDto.cs deleted file mode 100644 index 8268e4b172..0000000000 --- a/src/api/framework/Core/Audit/TrailDto.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.ObjectModel; -using System.Text.Json; - -namespace FSH.Framework.Core.Audit; -public class TrailDto() -{ - public Guid Id { get; set; } - public DateTimeOffset DateTime { get; set; } - public Guid UserId { get; set; } - public Dictionary KeyValues { get; } = []; - public Dictionary OldValues { get; } = []; - public Dictionary NewValues { get; } = []; - public Collection ModifiedProperties { get; } = []; - public TrailType Type { get; set; } - public string? TableName { get; set; } - - private static readonly JsonSerializerOptions SerializerOptions = new() - { - WriteIndented = false, - }; - - public AuditTrail ToAuditTrail() - { - return new() - { - Id = Guid.NewGuid(), - UserId = UserId, - Operation = Type.ToString(), - Entity = TableName, - DateTime = DateTime, - PrimaryKey = JsonSerializer.Serialize(KeyValues, SerializerOptions), - PreviousValues = OldValues.Count == 0 ? null : JsonSerializer.Serialize(OldValues, SerializerOptions), - NewValues = NewValues.Count == 0 ? null : JsonSerializer.Serialize(NewValues, SerializerOptions), - ModifiedProperties = ModifiedProperties.Count == 0 ? null : JsonSerializer.Serialize(ModifiedProperties, SerializerOptions) - }; - } -} diff --git a/src/api/framework/Core/Audit/TrailType.cs b/src/api/framework/Core/Audit/TrailType.cs deleted file mode 100644 index a98bfa29b6..0000000000 --- a/src/api/framework/Core/Audit/TrailType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Framework.Core.Audit; -public enum TrailType -{ - None = 0, - Create = 1, - Update = 2, - Delete = 3 -} diff --git a/src/api/framework/Core/Auth/Jwt/JwtOptions.cs b/src/api/framework/Core/Auth/Jwt/JwtOptions.cs deleted file mode 100644 index 5d99d6702f..0000000000 --- a/src/api/framework/Core/Auth/Jwt/JwtOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace FSH.Framework.Core.Auth.Jwt; -public class JwtOptions : IValidatableObject -{ - public string Key { get; set; } = string.Empty; - - public int TokenExpirationInMinutes { get; set; } = 60; - - public int RefreshTokenExpirationInDays { get; set; } = 7; - - public IEnumerable Validate(ValidationContext validationContext) - { - if (string.IsNullOrEmpty(Key)) - { - yield return new ValidationResult("No Key defined in JwtSettings config", [nameof(Key)]); - } - } -} diff --git a/src/api/framework/Core/Caching/CacheOptions.cs b/src/api/framework/Core/Caching/CacheOptions.cs deleted file mode 100644 index b861c2e06a..0000000000 --- a/src/api/framework/Core/Caching/CacheOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Core.Caching; - -public class CacheOptions -{ - public string Redis { get; set; } = string.Empty; -} diff --git a/src/api/framework/Core/Caching/CacheServiceExtensions.cs b/src/api/framework/Core/Caching/CacheServiceExtensions.cs deleted file mode 100644 index c03f94cc1f..0000000000 --- a/src/api/framework/Core/Caching/CacheServiceExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace FSH.Framework.Core.Caching; - -public static class CacheServiceExtensions -{ - public static T? GetOrSet(this ICacheService cache, string key, Func getItemCallback, TimeSpan? slidingExpiration = null) - { - T? value = cache.Get(key); - - if (value is not null) - { - return value; - } - - value = getItemCallback(); - - if (value is not null) - { - cache.Set(key, value, slidingExpiration); - } - - return value; - } - - public static async Task GetOrSetAsync(this ICacheService cache, string key, Func> task, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default) - { - T? value = await cache.GetAsync(key, cancellationToken); - - if (value is not null) - { - return value; - } - - value = await task(); - - if (value is not null) - { - await cache.SetAsync(key, value, slidingExpiration, cancellationToken); - } - - return value; - } -} diff --git a/src/api/framework/Core/Caching/ICacheService.cs b/src/api/framework/Core/Caching/ICacheService.cs deleted file mode 100644 index 54f3c09048..0000000000 --- a/src/api/framework/Core/Caching/ICacheService.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FSH.Framework.Core.Caching; - -public interface ICacheService -{ - T? Get(string key); - Task GetAsync(string key, CancellationToken token = default); - - void Refresh(string key); - Task RefreshAsync(string key, CancellationToken token = default); - - void Remove(string key); - Task RemoveAsync(string key, CancellationToken token = default); - - void Set(string key, T value, TimeSpan? slidingExpiration = null); - Task SetAsync(string key, T value, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/src/api/framework/Core/Core.csproj b/src/api/framework/Core/Core.csproj deleted file mode 100644 index 13d0f1dbc8..0000000000 --- a/src/api/framework/Core/Core.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - FSH.Framework.Core - FSH.Framework.Core - - - - - - - - - - - - - - - diff --git a/src/api/framework/Core/Domain/AuditableEntity.cs b/src/api/framework/Core/Domain/AuditableEntity.cs deleted file mode 100644 index 6639a02156..0000000000 --- a/src/api/framework/Core/Domain/AuditableEntity.cs +++ /dev/null @@ -1,18 +0,0 @@ -using FSH.Framework.Core.Domain.Contracts; - -namespace FSH.Framework.Core.Domain; - -public class AuditableEntity : BaseEntity, IAuditable, ISoftDeletable -{ - public DateTimeOffset Created { get; set; } - public Guid CreatedBy { get; set; } - public DateTimeOffset LastModified { get; set; } - public Guid? LastModifiedBy { get; set; } - public DateTimeOffset? Deleted { get; set; } - public Guid? DeletedBy { get; set; } -} - -public abstract class AuditableEntity : AuditableEntity -{ - protected AuditableEntity() => Id = Guid.NewGuid(); -} diff --git a/src/api/framework/Core/Domain/BaseEntity.cs b/src/api/framework/Core/Domain/BaseEntity.cs deleted file mode 100644 index 1c2e98daaf..0000000000 --- a/src/api/framework/Core/Domain/BaseEntity.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel.DataAnnotations.Schema; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Framework.Core.Domain; - -public abstract class BaseEntity : IEntity -{ - public TId Id { get; protected init; } = default!; - [NotMapped] - public Collection DomainEvents { get; } = new Collection(); - public void QueueDomainEvent(DomainEvent @event) - { - if (!DomainEvents.Contains(@event)) - DomainEvents.Add(@event); - } -} - -public abstract class BaseEntity : BaseEntity -{ - protected BaseEntity() => Id = Guid.NewGuid(); -} diff --git a/src/api/framework/Core/Domain/Contracts/IAggregateRoot.cs b/src/api/framework/Core/Domain/Contracts/IAggregateRoot.cs deleted file mode 100644 index cc98c00dba..0000000000 --- a/src/api/framework/Core/Domain/Contracts/IAggregateRoot.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Core.Domain.Contracts; - -// Apply this marker interface only to aggregate root entities -// Repositories will only work with aggregate roots, not their children -public interface IAggregateRoot : IEntity -{ -} diff --git a/src/api/framework/Core/Domain/Contracts/IAuditable.cs b/src/api/framework/Core/Domain/Contracts/IAuditable.cs deleted file mode 100644 index edfa8ab9f3..0000000000 --- a/src/api/framework/Core/Domain/Contracts/IAuditable.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Framework.Core.Domain.Contracts; - -public interface IAuditable -{ - DateTimeOffset Created { get; } - Guid CreatedBy { get; } - DateTimeOffset LastModified { get; } - Guid? LastModifiedBy { get; } -} diff --git a/src/api/framework/Core/Domain/Contracts/IEntity.cs b/src/api/framework/Core/Domain/Contracts/IEntity.cs deleted file mode 100644 index 1d48d306d6..0000000000 --- a/src/api/framework/Core/Domain/Contracts/IEntity.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.ObjectModel; -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Framework.Core.Domain.Contracts; - -public interface IEntity -{ - Collection DomainEvents { get; } -} - -public interface IEntity : IEntity -{ - TId Id { get; } -} diff --git a/src/api/framework/Core/Domain/Contracts/ISoftDeletable.cs b/src/api/framework/Core/Domain/Contracts/ISoftDeletable.cs deleted file mode 100644 index d129d02e4a..0000000000 --- a/src/api/framework/Core/Domain/Contracts/ISoftDeletable.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Core.Domain.Contracts; - -public interface ISoftDeletable -{ - DateTimeOffset? Deleted { get; set; } - Guid? DeletedBy { get; set; } -} diff --git a/src/api/framework/Core/Domain/Events/DomainEvent.cs b/src/api/framework/Core/Domain/Events/DomainEvent.cs deleted file mode 100644 index 5350854602..0000000000 --- a/src/api/framework/Core/Domain/Events/DomainEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Domain.Events; -public abstract record DomainEvent : IDomainEvent, INotification -{ - public DateTime RaisedOn { get; protected set; } = DateTime.UtcNow; -} diff --git a/src/api/framework/Core/Domain/Events/IDomainEvent.cs b/src/api/framework/Core/Domain/Events/IDomainEvent.cs deleted file mode 100644 index 68d4c8f6c2..0000000000 --- a/src/api/framework/Core/Domain/Events/IDomainEvent.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace FSH.Framework.Core.Domain.Events; -public interface IDomainEvent -{ -} diff --git a/src/api/framework/Core/Exceptions/CustomException.cs b/src/api/framework/Core/Exceptions/CustomException.cs deleted file mode 100644 index 4d1af9af97..0000000000 --- a/src/api/framework/Core/Exceptions/CustomException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Net; - -namespace FSH.Framework.Core.Exceptions; - -public class CustomException : Exception -{ - public List? ErrorMessages { get; } - - public HttpStatusCode StatusCode { get; } - - public CustomException(string message, List? errors = default, HttpStatusCode statusCode = HttpStatusCode.InternalServerError) - : base(message) - { - ErrorMessages = errors; - StatusCode = statusCode; - } -} diff --git a/src/api/framework/Core/Exceptions/ForbiddenException.cs b/src/api/framework/Core/Exceptions/ForbiddenException.cs deleted file mode 100644 index fdafead902..0000000000 --- a/src/api/framework/Core/Exceptions/ForbiddenException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Net; - -namespace FSH.Framework.Core.Exceptions; -public class ForbiddenException : FshException -{ - public ForbiddenException() - : base("unauthorized", [], HttpStatusCode.Forbidden) - { - } - public ForbiddenException(string message) - : base(message, [], HttpStatusCode.Forbidden) - { - } -} diff --git a/src/api/framework/Core/Exceptions/FshException.cs b/src/api/framework/Core/Exceptions/FshException.cs deleted file mode 100644 index 28597c5297..0000000000 --- a/src/api/framework/Core/Exceptions/FshException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Net; - -namespace FSH.Framework.Core.Exceptions; -public class FshException : Exception -{ - public IEnumerable ErrorMessages { get; } - - public HttpStatusCode StatusCode { get; } - - public FshException(string message, IEnumerable errors, HttpStatusCode statusCode = HttpStatusCode.InternalServerError) - : base(message) - { - ErrorMessages = errors; - StatusCode = statusCode; - } - - public FshException(string message) : base(message) - { - ErrorMessages = new List(); - } -} diff --git a/src/api/framework/Core/Exceptions/NotFoundException.cs b/src/api/framework/Core/Exceptions/NotFoundException.cs deleted file mode 100644 index 351e25cfc7..0000000000 --- a/src/api/framework/Core/Exceptions/NotFoundException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.ObjectModel; -using System.Net; - -namespace FSH.Framework.Core.Exceptions; -public class NotFoundException : FshException -{ - public NotFoundException(string message) - : base(message, new Collection(), HttpStatusCode.NotFound) - { - } -} diff --git a/src/api/framework/Core/Exceptions/UnauthorizedException.cs b/src/api/framework/Core/Exceptions/UnauthorizedException.cs deleted file mode 100644 index 559eb060c8..0000000000 --- a/src/api/framework/Core/Exceptions/UnauthorizedException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.ObjectModel; -using System.Net; - -namespace FSH.Framework.Core.Exceptions; -public class UnauthorizedException : FshException -{ - public UnauthorizedException() - : base("authentication failed", new Collection(), HttpStatusCode.Unauthorized) - { - } - public UnauthorizedException(string message) - : base(message, new Collection(), HttpStatusCode.Unauthorized) - { - } -} diff --git a/src/api/framework/Core/FshCore.cs b/src/api/framework/Core/FshCore.cs deleted file mode 100644 index 1891dc8d21..0000000000 --- a/src/api/framework/Core/FshCore.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Core; -public static class FshCore -{ - public static string Name { get; set; } = "FshCore"; -} diff --git a/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleCommand.cs b/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleCommand.cs deleted file mode 100644 index 5774c40ae9..0000000000 --- a/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleCommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Framework.Core.Identity.Roles.Features.CreateOrUpdateRole; - -public class CreateOrUpdateRoleCommand -{ - public string Id { get; set; } = default!; - public string Name { get; set; } = default!; - public string? Description { get; set; } -} diff --git a/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleValidator.cs b/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleValidator.cs deleted file mode 100644 index 68f4526661..0000000000 --- a/src/api/framework/Core/Identity/Roles/Features/CreateOrUpdateRole/CreateOrUpdateRoleValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Roles.Features.CreateOrUpdateRole; - -public class CreateOrUpdateRoleValidator : AbstractValidator -{ - public CreateOrUpdateRoleValidator() - { - RuleFor(x => x.Name).NotEmpty().WithMessage("Role name is required."); - } -} diff --git a/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsCommand.cs b/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsCommand.cs deleted file mode 100644 index 900c153956..0000000000 --- a/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsCommand.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Core.Identity.Roles.Features.UpdatePermissions; -public class UpdatePermissionsCommand -{ - public string RoleId { get; set; } = default!; - public List Permissions { get; set; } = default!; -} diff --git a/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsValidator.cs b/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsValidator.cs deleted file mode 100644 index 34b0b7f01c..0000000000 --- a/src/api/framework/Core/Identity/Roles/Features/UpdatePermissions/UpdatePermissionsValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Roles.Features.UpdatePermissions; -public class UpdatePermissionsValidator : AbstractValidator -{ - public UpdatePermissionsValidator() - { - RuleFor(r => r.RoleId) - .NotEmpty(); - RuleFor(r => r.Permissions) - .NotNull(); - } -} diff --git a/src/api/framework/Core/Identity/Roles/IRoleService.cs b/src/api/framework/Core/Identity/Roles/IRoleService.cs deleted file mode 100644 index dca61839af..0000000000 --- a/src/api/framework/Core/Identity/Roles/IRoleService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using FSH.Framework.Core.Identity.Roles.Features.CreateOrUpdateRole; -using FSH.Framework.Core.Identity.Roles.Features.UpdatePermissions; - -namespace FSH.Framework.Core.Identity.Roles; - -public interface IRoleService -{ - Task> GetRolesAsync(); - Task GetRoleAsync(string id); - Task CreateOrUpdateRoleAsync(CreateOrUpdateRoleCommand command); - Task DeleteRoleAsync(string id); - Task GetWithPermissionsAsync(string id, CancellationToken cancellationToken); - - Task UpdatePermissionsAsync(UpdatePermissionsCommand request); -} - diff --git a/src/api/framework/Core/Identity/Roles/RoleDto.cs b/src/api/framework/Core/Identity/Roles/RoleDto.cs deleted file mode 100644 index 0a0fc7559b..0000000000 --- a/src/api/framework/Core/Identity/Roles/RoleDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Framework.Core.Identity.Roles; - -public class RoleDto -{ - public string Id { get; set; } = default!; - public string Name { get; set; } = default!; - public string? Description { get; set; } - public List? Permissions { get; set; } -} diff --git a/src/api/framework/Core/Identity/Tokens/Features/Generate/TokenGenerationCommand.cs b/src/api/framework/Core/Identity/Tokens/Features/Generate/TokenGenerationCommand.cs deleted file mode 100644 index dccc1e15d7..0000000000 --- a/src/api/framework/Core/Identity/Tokens/Features/Generate/TokenGenerationCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; -using FluentValidation; -using FSH.Starter.Shared.Authorization; - -namespace FSH.Framework.Core.Identity.Tokens.Features.Generate; -public record TokenGenerationCommand( - [property: DefaultValue(TenantConstants.Root.EmailAddress)] string Email, - [property: DefaultValue(TenantConstants.DefaultPassword)] string Password); - -public class GenerateTokenValidator : AbstractValidator -{ - public GenerateTokenValidator() - { - RuleFor(p => p.Email).Cascade(CascadeMode.Stop).NotEmpty().EmailAddress(); - - RuleFor(p => p.Password).Cascade(CascadeMode.Stop).NotEmpty(); - } -} diff --git a/src/api/framework/Core/Identity/Tokens/Features/Refresh/RefreshTokenCommand.cs b/src/api/framework/Core/Identity/Tokens/Features/Refresh/RefreshTokenCommand.cs deleted file mode 100644 index 8fc45b8d24..0000000000 --- a/src/api/framework/Core/Identity/Tokens/Features/Refresh/RefreshTokenCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Tokens.Features.Refresh; -public record RefreshTokenCommand(string Token, string RefreshToken); - -public class RefreshTokenValidator : AbstractValidator -{ - public RefreshTokenValidator() - { - RuleFor(p => p.Token).Cascade(CascadeMode.Stop).NotEmpty(); - - RuleFor(p => p.RefreshToken).Cascade(CascadeMode.Stop).NotEmpty(); - } -} diff --git a/src/api/framework/Core/Identity/Tokens/ITokenService.cs b/src/api/framework/Core/Identity/Tokens/ITokenService.cs deleted file mode 100644 index 86665ec818..0000000000 --- a/src/api/framework/Core/Identity/Tokens/ITokenService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FSH.Framework.Core.Identity.Tokens.Features.Generate; -using FSH.Framework.Core.Identity.Tokens.Features.Refresh; -using FSH.Framework.Core.Identity.Tokens.Models; - -namespace FSH.Framework.Core.Identity.Tokens; -public interface ITokenService -{ - Task GenerateTokenAsync(TokenGenerationCommand request, string ipAddress, CancellationToken cancellationToken); - Task RefreshTokenAsync(RefreshTokenCommand request, string ipAddress, CancellationToken cancellationToken); - -} diff --git a/src/api/framework/Core/Identity/Tokens/Models/TokenResponse.cs b/src/api/framework/Core/Identity/Tokens/Models/TokenResponse.cs deleted file mode 100644 index fc56f00d89..0000000000 --- a/src/api/framework/Core/Identity/Tokens/Models/TokenResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Identity.Tokens.Models; -public record TokenResponse(string Token, string RefreshToken, DateTime RefreshTokenExpiryTime); diff --git a/src/api/framework/Core/Identity/Users/Abstractions/IUserService.cs b/src/api/framework/Core/Identity/Users/Abstractions/IUserService.cs deleted file mode 100644 index 95fbf9f577..0000000000 --- a/src/api/framework/Core/Identity/Users/Abstractions/IUserService.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Security.Claims; -using FSH.Framework.Core.Identity.Users.Dtos; -using FSH.Framework.Core.Identity.Users.Features.AssignUserRole; -using FSH.Framework.Core.Identity.Users.Features.ChangePassword; -using FSH.Framework.Core.Identity.Users.Features.ForgotPassword; -using FSH.Framework.Core.Identity.Users.Features.RegisterUser; -using FSH.Framework.Core.Identity.Users.Features.ResetPassword; -using FSH.Framework.Core.Identity.Users.Features.ToggleUserStatus; -using FSH.Framework.Core.Identity.Users.Features.UpdateUser; - -namespace FSH.Framework.Core.Identity.Users.Abstractions; -public interface IUserService -{ - Task ExistsWithNameAsync(string name); - Task ExistsWithEmailAsync(string email, string? exceptId = null); - Task ExistsWithPhoneNumberAsync(string phoneNumber, string? exceptId = null); - Task> GetListAsync(CancellationToken cancellationToken); - Task GetCountAsync(CancellationToken cancellationToken); - Task GetAsync(string userId, CancellationToken cancellationToken); - Task ToggleStatusAsync(ToggleUserStatusCommand request, CancellationToken cancellationToken); - Task GetOrCreateFromPrincipalAsync(ClaimsPrincipal principal); - Task RegisterAsync(RegisterUserCommand request, string origin, CancellationToken cancellationToken); - Task UpdateAsync(UpdateUserCommand request, string userId); - Task DeleteAsync(string userId); - Task ConfirmEmailAsync(string userId, string code, string tenant, CancellationToken cancellationToken); - Task ConfirmPhoneNumberAsync(string userId, string code); - - // permisions - Task HasPermissionAsync(string userId, string permission, CancellationToken cancellationToken = default); - - // passwords - Task ForgotPasswordAsync(ForgotPasswordCommand request, string origin, CancellationToken cancellationToken); - Task ResetPasswordAsync(ResetPasswordCommand request, CancellationToken cancellationToken); - Task?> GetPermissionsAsync(string userId, CancellationToken cancellationToken); - - Task ChangePasswordAsync(ChangePasswordCommand request, string userId); - Task AssignRolesAsync(string userId, AssignUserRoleCommand request, CancellationToken cancellationToken); - Task> GetUserRolesAsync(string userId, CancellationToken cancellationToken); -} diff --git a/src/api/framework/Core/Identity/Users/Dtos/UserDetail.cs b/src/api/framework/Core/Identity/Users/Dtos/UserDetail.cs deleted file mode 100644 index 23941ad86c..0000000000 --- a/src/api/framework/Core/Identity/Users/Dtos/UserDetail.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Dtos; -public class UserDetail -{ - public Guid Id { get; set; } - - public string? UserName { get; set; } - - public string? FirstName { get; set; } - - public string? LastName { get; set; } - - public string? Email { get; set; } - - public bool IsActive { get; set; } = true; - - public bool EmailConfirmed { get; set; } - - public string? PhoneNumber { get; set; } - - public Uri? ImageUrl { get; set; } -} diff --git a/src/api/framework/Core/Identity/Users/Dtos/UserRoleDetail.cs b/src/api/framework/Core/Identity/Users/Dtos/UserRoleDetail.cs deleted file mode 100644 index 935fd79b6e..0000000000 --- a/src/api/framework/Core/Identity/Users/Dtos/UserRoleDetail.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Dtos; -public class UserRoleDetail -{ - public string? RoleId { get; set; } - public string? RoleName { get; set; } - public string? Description { get; set; } - public bool Enabled { get; set; } -} diff --git a/src/api/framework/Core/Identity/Users/Features/AssignUserRole/AssignUserRoleCommand.cs b/src/api/framework/Core/Identity/Users/Features/AssignUserRole/AssignUserRoleCommand.cs deleted file mode 100644 index 34f3fadb89..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/AssignUserRole/AssignUserRoleCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Dtos; - -namespace FSH.Framework.Core.Identity.Users.Features.AssignUserRole; -public class AssignUserRoleCommand -{ - public List UserRoles { get; set; } = new(); -} diff --git a/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordCommand.cs b/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordCommand.cs deleted file mode 100644 index 82abe1323c..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Features.ChangePassword; -public class ChangePasswordCommand -{ - public string Password { get; set; } = default!; - public string NewPassword { get; set; } = default!; - public string ConfirmNewPassword { get; set; } = default!; -} diff --git a/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordValidator.cs b/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordValidator.cs deleted file mode 100644 index 9d52f78856..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ChangePassword/ChangePasswordValidator.cs +++ /dev/null @@ -1,18 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Users.Features.ChangePassword; -public class ChangePasswordValidator : AbstractValidator -{ - public ChangePasswordValidator() - { - RuleFor(p => p.Password) - .NotEmpty(); - - RuleFor(p => p.NewPassword) - .NotEmpty(); - - RuleFor(p => p.ConfirmNewPassword) - .Equal(p => p.NewPassword) - .WithMessage("passwords do not match."); - } -} diff --git a/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordCommand.cs b/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordCommand.cs deleted file mode 100644 index 5419a554fb..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordCommand.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Features.ForgotPassword; -public class ForgotPasswordCommand -{ - public string Email { get; set; } = default!; -} diff --git a/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordValidator.cs b/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordValidator.cs deleted file mode 100644 index 2df57f5be4..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ForgotPassword/ForgotPasswordValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Users.Features.ForgotPassword; -public class ForgotPasswordValidator : AbstractValidator -{ - public ForgotPasswordValidator() - { - RuleFor(p => p.Email).Cascade(CascadeMode.Stop) - .NotEmpty() - .EmailAddress(); - } -} diff --git a/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserCommand.cs b/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserCommand.cs deleted file mode 100644 index 34089d0470..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserCommand.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; -using MediatR; - -namespace FSH.Framework.Core.Identity.Users.Features.RegisterUser; -public class RegisterUserCommand : IRequest -{ - public string FirstName { get; set; } = default!; - public string LastName { get; set; } = default!; - public string Email { get; set; } = default!; - public string UserName { get; set; } = default!; - public string Password { get; set; } = default!; - public string ConfirmPassword { get; set; } = default!; - public string? PhoneNumber { get; set; } - - [JsonIgnore] - public string? Origin { get; set; } -} diff --git a/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserResponse.cs b/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserResponse.cs deleted file mode 100644 index 967539ae78..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/RegisterUser/RegisterUserResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Features.RegisterUser; -public record RegisterUserResponse(string UserId); diff --git a/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordCommand.cs b/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordCommand.cs deleted file mode 100644 index 244aff2e93..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Features.ResetPassword; -public class ResetPasswordCommand -{ - public string Email { get; set; } = default!; - - public string Password { get; set; } = default!; - - public string Token { get; set; } = default!; -} diff --git a/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordValidator.cs b/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordValidator.cs deleted file mode 100644 index 4141905651..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ResetPassword/ResetPasswordValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Identity.Users.Features.ResetPassword; - -public class ResetPasswordValidator : AbstractValidator -{ - public ResetPasswordValidator() - { - RuleFor(x => x.Email).NotEmpty().EmailAddress(); - RuleFor(x => x.Password).NotEmpty(); - RuleFor(x => x.Token).NotEmpty(); - } -} diff --git a/src/api/framework/Core/Identity/Users/Features/ToggleUserStatus/ToggleUserStatusCommand.cs b/src/api/framework/Core/Identity/Users/Features/ToggleUserStatus/ToggleUserStatusCommand.cs deleted file mode 100644 index 8b3697293e..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/ToggleUserStatus/ToggleUserStatusCommand.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Core.Identity.Users.Features.ToggleUserStatus; -public class ToggleUserStatusCommand -{ - public bool ActivateUser { get; set; } - public string? UserId { get; set; } -} diff --git a/src/api/framework/Core/Identity/Users/Features/UpdateUser/UpdateUserCommand.cs b/src/api/framework/Core/Identity/Users/Features/UpdateUser/UpdateUserCommand.cs deleted file mode 100644 index 470516218e..0000000000 --- a/src/api/framework/Core/Identity/Users/Features/UpdateUser/UpdateUserCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FSH.Framework.Core.Storage.File.Features; -using MediatR; - -namespace FSH.Framework.Core.Identity.Users.Features.UpdateUser; -public class UpdateUserCommand : IRequest -{ - public string Id { get; set; } = default!; - public string? FirstName { get; set; } - public string? LastName { get; set; } - public string? PhoneNumber { get; set; } - public string? Email { get; set; } - public FileUploadCommand? Image { get; set; } - public bool DeleteCurrentImage { get; set; } -} diff --git a/src/api/framework/Core/Jobs/IJobService.cs b/src/api/framework/Core/Jobs/IJobService.cs deleted file mode 100644 index 7016ae79d5..0000000000 --- a/src/api/framework/Core/Jobs/IJobService.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Linq.Expressions; - -namespace FSH.Framework.Core.Jobs; - -public interface IJobService -{ - bool Delete(string jobId); - - bool Delete(string jobId, string fromState); - - string Enqueue(Expression methodCall); - - string Enqueue(string queue, Expression> methodCall); - - string Enqueue(Expression> methodCall); - - string Enqueue(Expression> methodCall); - - string Enqueue(Expression> methodCall); - - bool Requeue(string jobId); - - bool Requeue(string jobId, string fromState); - - string Schedule(Expression methodCall, TimeSpan delay); - - string Schedule(Expression> methodCall, TimeSpan delay); - - string Schedule(Expression methodCall, DateTimeOffset enqueueAt); - - string Schedule(Expression> methodCall, DateTimeOffset enqueueAt); - - string Schedule(Expression> methodCall, TimeSpan delay); - - string Schedule(Expression> methodCall, TimeSpan delay); - - string Schedule(Expression> methodCall, DateTimeOffset enqueueAt); - - string Schedule(Expression> methodCall, DateTimeOffset enqueueAt); -} diff --git a/src/api/framework/Core/Mail/IMailService.cs b/src/api/framework/Core/Mail/IMailService.cs deleted file mode 100644 index c5e000951b..0000000000 --- a/src/api/framework/Core/Mail/IMailService.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Core.Mail; -public interface IMailService -{ - Task SendAsync(MailRequest request, CancellationToken ct); -} diff --git a/src/api/framework/Core/Mail/MailOptions.cs b/src/api/framework/Core/Mail/MailOptions.cs deleted file mode 100644 index 4b01169572..0000000000 --- a/src/api/framework/Core/Mail/MailOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FSH.Framework.Core.Mail; -public class MailOptions -{ - public string? From { get; set; } - - public string? Host { get; set; } - - public int Port { get; set; } - - public string? UserName { get; set; } - - public string? Password { get; set; } - - public string? DisplayName { get; set; } -} diff --git a/src/api/framework/Core/Mail/MailRequest.cs b/src/api/framework/Core/Mail/MailRequest.cs deleted file mode 100644 index 662dcd4010..0000000000 --- a/src/api/framework/Core/Mail/MailRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.ObjectModel; - -namespace FSH.Framework.Core.Mail; -public class MailRequest(Collection to, string subject, string? body = null, string? from = null, string? displayName = null, string? replyTo = null, string? replyToName = null, Collection? bcc = null, Collection? cc = null, IDictionary? attachmentData = null, IDictionary? headers = null) -{ - public Collection To { get; } = to; - - public string Subject { get; } = subject; - - public string? Body { get; } = body; - - public string? From { get; } = from; - - public string? DisplayName { get; } = displayName; - - public string? ReplyTo { get; } = replyTo; - - public string? ReplyToName { get; } = replyToName; - - public Collection Bcc { get; } = bcc ?? new Collection(); - - public Collection Cc { get; } = cc ?? new Collection(); - - public IDictionary AttachmentData { get; } = attachmentData ?? new Dictionary(); - - public IDictionary Headers { get; } = headers ?? new Dictionary(); -} diff --git a/src/api/framework/Core/Origin/OriginOptions.cs b/src/api/framework/Core/Origin/OriginOptions.cs deleted file mode 100644 index 97e1c35423..0000000000 --- a/src/api/framework/Core/Origin/OriginOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Core.Origin; - -public class OriginOptions -{ - public Uri? OriginUrl { get; set; } -} diff --git a/src/api/framework/Core/Paging/BaseFilter.cs b/src/api/framework/Core/Paging/BaseFilter.cs deleted file mode 100644 index 2bb5b099be..0000000000 --- a/src/api/framework/Core/Paging/BaseFilter.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public class BaseFilter -{ - /// - /// Column Wise Search is Supported. - /// - public Search? AdvancedSearch { get; set; } - - /// - /// Keyword to Search in All the available columns of the Resource. - /// - public string? Keyword { get; set; } - - /// - /// Advanced column filtering with logical operators and query operators is supported. - /// - public Filter? AdvancedFilter { get; set; } -} diff --git a/src/api/framework/Core/Paging/Extensions.cs b/src/api/framework/Core/Paging/Extensions.cs deleted file mode 100644 index a9c5544eb9..0000000000 --- a/src/api/framework/Core/Paging/Extensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ardalis.Specification; - -namespace FSH.Framework.Core.Paging; -public static class Extensions -{ - public static async Task> PaginatedListAsync( - this IReadRepositoryBase repository, ISpecification spec, PaginationFilter filter, CancellationToken cancellationToken = default) - where T : class - where TDestination : class - { - ArgumentNullException.ThrowIfNull(repository); - - var items = await repository.ListAsync(spec, cancellationToken).ConfigureAwait(false); - int totalCount = await repository.CountAsync(spec, cancellationToken).ConfigureAwait(false); - - return new PagedList(items, filter.PageNumber, filter.PageSize, totalCount); - } -} diff --git a/src/api/framework/Core/Paging/Filter.cs b/src/api/framework/Core/Paging/Filter.cs deleted file mode 100644 index fdbdc29387..0000000000 --- a/src/api/framework/Core/Paging/Filter.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public static class FilterOperator -{ - public const string EQ = "eq"; - public const string NEQ = "neq"; - public const string LT = "lt"; - public const string LTE = "lte"; - public const string GT = "gt"; - public const string GTE = "gte"; - public const string STARTSWITH = "startswith"; - public const string ENDSWITH = "endswith"; - public const string CONTAINS = "contains"; -} - -public static class FilterLogic -{ - public const string AND = "and"; - public const string OR = "or"; - public const string XOR = "xor"; -} - -public class Filter -{ - public string? Logic { get; set; } - - public IEnumerable? Filters { get; set; } - - public string? Field { get; set; } - - public string? Operator { get; set; } - - public object? Value { get; set; } -} diff --git a/src/api/framework/Core/Paging/IPageRequest.cs b/src/api/framework/Core/Paging/IPageRequest.cs deleted file mode 100644 index c4a2a7f147..0000000000 --- a/src/api/framework/Core/Paging/IPageRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public interface IPageRequest -{ - int PageNumber { get; init; } - int PageSize { get; init; } - string? Filters { get; init; } - string? SortOrder { get; init; } -} diff --git a/src/api/framework/Core/Paging/IPagedList.cs b/src/api/framework/Core/Paging/IPagedList.cs deleted file mode 100644 index e2950b3984..0000000000 --- a/src/api/framework/Core/Paging/IPagedList.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public interface IPagedList - where T : class -{ - int TotalPages { get; } - bool HasPrevious { get; } - bool HasNext { get; } - IReadOnlyList Items { get; init; } - int TotalCount { get; init; } - int PageNumber { get; init; } - int PageSize { get; init; } - - IPagedList MapTo(Func map) - where TR : class; - IPagedList MapTo() - where TR : class; -} diff --git a/src/api/framework/Core/Paging/PagedList.cs b/src/api/framework/Core/Paging/PagedList.cs deleted file mode 100644 index 7f48292f7c..0000000000 --- a/src/api/framework/Core/Paging/PagedList.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Mapster; - -namespace FSH.Framework.Core.Paging; - -public record PagedList(IReadOnlyList Items, int PageNumber, int PageSize, int TotalCount) : IPagedList - where T : class -{ - public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize); - public bool HasPrevious => PageNumber > 1; - public bool HasNext => PageNumber < TotalPages; - public IPagedList MapTo(Func map) - where TR : class - { - return new PagedList(Items.Select(map).ToList(), PageNumber, PageSize, TotalCount); - } - public IPagedList MapTo() - where TR : class - { - return new PagedList(Items.Adapt>(), PageNumber, PageSize, TotalCount); - } -} diff --git a/src/api/framework/Core/Paging/PaginationFilter.cs b/src/api/framework/Core/Paging/PaginationFilter.cs deleted file mode 100644 index 13be4026ee..0000000000 --- a/src/api/framework/Core/Paging/PaginationFilter.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public class PaginationFilter : BaseFilter -{ - public int PageNumber { get; set; } - - public int PageSize { get; set; } = int.MaxValue; - public string[]? OrderBy { get; set; } -} - -public static class PaginationFilterExtensions -{ - public static bool HasOrderBy(this PaginationFilter filter) => - filter.OrderBy?.Any() is true; -} diff --git a/src/api/framework/Core/Paging/Search.cs b/src/api/framework/Core/Paging/Search.cs deleted file mode 100644 index a2f2624980..0000000000 --- a/src/api/framework/Core/Paging/Search.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Core.Paging; - -public class Search -{ - public List Fields { get; set; } = new(); - public string? Keyword { get; set; } -} diff --git a/src/api/framework/Core/Persistence/DatabaseOptions.cs b/src/api/framework/Core/Persistence/DatabaseOptions.cs deleted file mode 100644 index 5be4fb9e02..0000000000 --- a/src/api/framework/Core/Persistence/DatabaseOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace FSH.Framework.Core.Persistence; -public class DatabaseOptions : IValidatableObject -{ - public string Provider { get; set; } = "postgresql"; - public string ConnectionString { get; set; } = string.Empty; - - public IEnumerable Validate(ValidationContext validationContext) - { - if (string.IsNullOrEmpty(ConnectionString)) - { - yield return new ValidationResult("connection string cannot be empty.", new[] { nameof(ConnectionString) }); - } - } -} diff --git a/src/api/framework/Core/Persistence/IConnectionStringValidator.cs b/src/api/framework/Core/Persistence/IConnectionStringValidator.cs deleted file mode 100644 index 413e4fc76c..0000000000 --- a/src/api/framework/Core/Persistence/IConnectionStringValidator.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Core.Persistence; -public interface IConnectionStringValidator -{ - bool TryValidate(string connectionString, string? dbProvider = null); -} diff --git a/src/api/framework/Core/Persistence/IDbInitializer.cs b/src/api/framework/Core/Persistence/IDbInitializer.cs deleted file mode 100644 index ed4a08c844..0000000000 --- a/src/api/framework/Core/Persistence/IDbInitializer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Core.Persistence; -public interface IDbInitializer -{ - Task MigrateAsync(CancellationToken cancellationToken); - Task SeedAsync(CancellationToken cancellationToken); -} diff --git a/src/api/framework/Core/Persistence/IRepository.cs b/src/api/framework/Core/Persistence/IRepository.cs deleted file mode 100644 index 3915bb3caa..0000000000 --- a/src/api/framework/Core/Persistence/IRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Ardalis.Specification; -using FSH.Framework.Core.Domain.Contracts; - -namespace FSH.Framework.Core.Persistence; -public interface IRepository : IRepositoryBase - where T : class, IAggregateRoot -{ -} - -public interface IReadRepository : IReadRepositoryBase - where T : class, IAggregateRoot -{ -} diff --git a/src/api/framework/Core/Specifications/EntitiesByBaseFilterSpec.cs b/src/api/framework/Core/Specifications/EntitiesByBaseFilterSpec.cs deleted file mode 100644 index 643bcb675a..0000000000 --- a/src/api/framework/Core/Specifications/EntitiesByBaseFilterSpec.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ardalis.Specification; -using FSH.Framework.Core.Paging; - -namespace FSH.Framework.Core.Specifications; - -public class EntitiesByBaseFilterSpec : Specification -{ - public EntitiesByBaseFilterSpec(BaseFilter filter) => - Query.SearchBy(filter); -} - -public class EntitiesByBaseFilterSpec : Specification -{ - public EntitiesByBaseFilterSpec(BaseFilter filter) => - Query.SearchBy(filter); -} diff --git a/src/api/framework/Core/Specifications/EntitiesByPaginationFilterSpec.cs b/src/api/framework/Core/Specifications/EntitiesByPaginationFilterSpec.cs deleted file mode 100644 index abdf49eeb7..0000000000 --- a/src/api/framework/Core/Specifications/EntitiesByPaginationFilterSpec.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FSH.Framework.Core.Paging; - -namespace FSH.Framework.Core.Specifications; - -public class EntitiesByPaginationFilterSpec : EntitiesByBaseFilterSpec -{ - public EntitiesByPaginationFilterSpec(PaginationFilter filter) - : base(filter) => - Query.PaginateBy(filter); -} - -public class EntitiesByPaginationFilterSpec : EntitiesByBaseFilterSpec -{ - public EntitiesByPaginationFilterSpec(PaginationFilter filter) - : base(filter) => - Query.PaginateBy(filter); -} diff --git a/src/api/framework/Core/Specifications/SpecificationBuilderExtensions.cs b/src/api/framework/Core/Specifications/SpecificationBuilderExtensions.cs deleted file mode 100644 index 81b352056d..0000000000 --- a/src/api/framework/Core/Specifications/SpecificationBuilderExtensions.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; -using System.Text.Json; -using Ardalis.Specification; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Paging; - -namespace FSH.Framework.Core.Specifications; - -// See https://github.com/ardalis/Specification/issues/53 -public static class SpecificationBuilderExtensions -{ - public static ISpecificationBuilder SearchBy(this ISpecificationBuilder query, BaseFilter filter) => - query - .SearchByKeyword(filter.Keyword) - .AdvancedSearch(filter.AdvancedSearch) - .AdvancedFilter(filter.AdvancedFilter); - - public static ISpecificationBuilder PaginateBy(this ISpecificationBuilder query, PaginationFilter filter) - { - if (filter.PageNumber <= 0) - { - filter.PageNumber = 1; - } - - if (filter.PageSize <= 0) - { - filter.PageSize = 10; - } - - if (filter.PageNumber > 1) - { - query = query.Skip((filter.PageNumber - 1) * filter.PageSize); - } - - return query - .Take(filter.PageSize) - .OrderBy(filter.OrderBy); - } - - public static IOrderedSpecificationBuilder SearchByKeyword( - this ISpecificationBuilder specificationBuilder, - string? keyword) => - specificationBuilder.AdvancedSearch(new Search { Keyword = keyword }); - - public static IOrderedSpecificationBuilder AdvancedSearch( - this ISpecificationBuilder specificationBuilder, - Search? search) - { - if (!string.IsNullOrEmpty(search?.Keyword)) - { - if (search.Fields?.Any() is true) - { - // search seleted fields (can contain deeper nested fields) - foreach (string field in search.Fields) - { - var paramExpr = Expression.Parameter(typeof(T)); - MemberExpression propertyExpr = GetPropertyExpression(field, paramExpr); - - specificationBuilder.AddSearchPropertyByKeyword(propertyExpr, paramExpr, search.Keyword); - } - } - else - { - // search all fields (only first level) - foreach (var property in typeof(T).GetProperties() - .Where(prop => (Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType) is { } propertyType - && !propertyType.IsEnum - && Type.GetTypeCode(propertyType) != TypeCode.Object)) - { - var paramExpr = Expression.Parameter(typeof(T)); - var propertyExpr = Expression.Property(paramExpr, property); - - specificationBuilder.AddSearchPropertyByKeyword(propertyExpr, paramExpr, search.Keyword); - } - } - } - - return new OrderedSpecificationBuilder(specificationBuilder.Specification); - } - - private static void AddSearchPropertyByKeyword( - this ISpecificationBuilder specificationBuilder, - Expression propertyExpr, - ParameterExpression paramExpr, - string keyword, - string operatorSearch = FilterOperator.CONTAINS) - { - if (propertyExpr is not MemberExpression memberExpr || memberExpr.Member is not PropertyInfo property) - { - throw new ArgumentException("propertyExpr must be a property expression.", nameof(propertyExpr)); - } - - string searchTerm = operatorSearch switch - { - FilterOperator.STARTSWITH => $"{keyword.ToLower()}%", - FilterOperator.ENDSWITH => $"%{keyword.ToLower()}", - FilterOperator.CONTAINS => $"%{keyword.ToLower()}%", - _ => throw new ArgumentException("operatorSearch is not valid.", nameof(operatorSearch)) - }; - - // Generate lambda [ x => x.Property ] for string properties - // or [ x => ((object)x.Property) == null ? null : x.Property.ToString() ] for other properties - Expression selectorExpr = - property.PropertyType == typeof(string) - ? propertyExpr - : Expression.Condition( - Expression.Equal(Expression.Convert(propertyExpr, typeof(object)), Expression.Constant(null, typeof(object))), - Expression.Constant(null, typeof(string)), - Expression.Call(propertyExpr, "ToString", null, null)); - - var toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes); - Expression callToLowerMethod = Expression.Call(selectorExpr, toLowerMethod!); - - var selector = Expression.Lambda>(callToLowerMethod, paramExpr); - - ((List>)specificationBuilder.Specification.SearchCriterias) - .Add(new SearchExpressionInfo(selector, searchTerm, 1)); - } - - public static IOrderedSpecificationBuilder AdvancedFilter( - this ISpecificationBuilder specificationBuilder, - Filter? filter) - { - if (filter is not null) - { - var parameter = Expression.Parameter(typeof(T)); - - Expression binaryExpresioFilter; - - if (!string.IsNullOrEmpty(filter.Logic)) - { - if (filter.Filters is null) throw new CustomException("The Filters attribute is required when declaring a logic"); - binaryExpresioFilter = CreateFilterExpression(filter.Logic, filter.Filters, parameter); - } - else - { - var filterValid = GetValidFilter(filter); - binaryExpresioFilter = CreateFilterExpression(filterValid.Field!, filterValid.Operator!, filterValid.Value, parameter); - } - - ((List>)specificationBuilder.Specification.WhereExpressions) - .Add(new WhereExpressionInfo(Expression.Lambda>(binaryExpresioFilter, parameter))); - } - - return new OrderedSpecificationBuilder(specificationBuilder.Specification); - } - - private static Expression CreateFilterExpression( - string logic, - IEnumerable filters, - ParameterExpression parameter) - { - Expression filterExpression = default!; - - foreach (var filter in filters) - { - Expression bExpresionFilter; - - if (!string.IsNullOrEmpty(filter.Logic)) - { - if (filter.Filters is null) throw new CustomException("The Filters attribute is required when declaring a logic"); - bExpresionFilter = CreateFilterExpression(filter.Logic, filter.Filters, parameter); - } - else - { - var filterValid = GetValidFilter(filter); - bExpresionFilter = CreateFilterExpression(filterValid.Field!, filterValid.Operator!, filterValid.Value, parameter); - } - - filterExpression = filterExpression is null ? bExpresionFilter : CombineFilter(logic, filterExpression, bExpresionFilter); - } - - return filterExpression; - } - - private static Expression CreateFilterExpression( - string field, - string filterOperator, - object? value, - ParameterExpression parameter) - { - var propertyExpresion = GetPropertyExpression(field, parameter); - var valueExpresion = GeValuetExpression(field, value, propertyExpresion.Type); - return CreateFilterExpression(propertyExpresion, valueExpresion, filterOperator); - } - - private static Expression CreateFilterExpression( - Expression memberExpression, - Expression constantExpression, - string filterOperator) - { - if (memberExpression.Type == typeof(string)) - { - constantExpression = Expression.Call(constantExpression, "ToLower", null); - memberExpression = Expression.Call(memberExpression, "ToLower", null); - } - - return filterOperator switch - { - FilterOperator.EQ => Expression.Equal(memberExpression, constantExpression), - FilterOperator.NEQ => Expression.NotEqual(memberExpression, constantExpression), - FilterOperator.LT => Expression.LessThan(memberExpression, constantExpression), - FilterOperator.LTE => Expression.LessThanOrEqual(memberExpression, constantExpression), - FilterOperator.GT => Expression.GreaterThan(memberExpression, constantExpression), - FilterOperator.GTE => Expression.GreaterThanOrEqual(memberExpression, constantExpression), - FilterOperator.CONTAINS => Expression.Call(memberExpression, "Contains", null, constantExpression), - FilterOperator.STARTSWITH => Expression.Call(memberExpression, "StartsWith", null, constantExpression), - FilterOperator.ENDSWITH => Expression.Call(memberExpression, "EndsWith", null, constantExpression), - _ => throw new CustomException("Filter Operator is not valid."), - }; - } - - private static Expression CombineFilter( - string filterOperator, - Expression bExpresionBase, - Expression bExpresion) => filterOperator switch - { - FilterLogic.AND => Expression.And(bExpresionBase, bExpresion), - FilterLogic.OR => Expression.Or(bExpresionBase, bExpresion), - FilterLogic.XOR => Expression.ExclusiveOr(bExpresionBase, bExpresion), - _ => throw new ArgumentException("FilterLogic is not valid."), - }; - - private static MemberExpression GetPropertyExpression( - string propertyName, - ParameterExpression parameter) - { - Expression propertyExpression = parameter; - foreach (string member in propertyName.Split('.')) - { - propertyExpression = Expression.PropertyOrField(propertyExpression, member); - } - - return (MemberExpression)propertyExpression; - } - - private static string GetStringFromJsonElement(object value) - => ((JsonElement)value).GetString()!; - - private static ConstantExpression GeValuetExpression( - string field, - object? value, - Type propertyType) - { - if (value == null) return Expression.Constant(null, propertyType); - - if (propertyType.IsEnum) - { - string? stringEnum = GetStringFromJsonElement(value); - - if (!Enum.TryParse(propertyType, stringEnum, true, out object? valueparsed)) throw new CustomException(string.Format("Value {0} is not valid for {1}", value, field)); - - return Expression.Constant(valueparsed, propertyType); - } - - if (propertyType == typeof(Guid)) - { - string? stringGuid = GetStringFromJsonElement(value); - - if (!Guid.TryParse(stringGuid, out Guid valueparsed)) throw new CustomException(string.Format("Value {0} is not valid for {1}", value, field)); - - return Expression.Constant(valueparsed, propertyType); - } - - if (propertyType == typeof(string)) - { - string? text = GetStringFromJsonElement(value); - - return Expression.Constant(text, propertyType); - } - - if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)) - { - string? text = GetStringFromJsonElement(value); - return Expression.Constant(ChangeType(text, propertyType), propertyType); - } - - return Expression.Constant(ChangeType(((JsonElement)value).GetRawText(), propertyType), propertyType); - } - - public static dynamic? ChangeType(object value, Type conversion) - { - var t = conversion; - - if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) - { - if (value == null) - { - return null; - } - - t = Nullable.GetUnderlyingType(t); - } - - return Convert.ChangeType(value, t!); - } - - private static Filter GetValidFilter(Filter filter) - { - if (string.IsNullOrEmpty(filter.Field)) throw new CustomException("The field attribute is required when declaring a filter"); - if (string.IsNullOrEmpty(filter.Operator)) throw new CustomException("The Operator attribute is required when declaring a filter"); - return filter; - } - - public static IOrderedSpecificationBuilder OrderBy( - this ISpecificationBuilder specificationBuilder, - string[]? orderByFields) - { - if (orderByFields is not null) - { - foreach (var field in ParseOrderBy(orderByFields)) - { - var paramExpr = Expression.Parameter(typeof(T)); - - Expression propertyExpr = paramExpr; - foreach (string member in field.Key.Split('.')) - { - propertyExpr = Expression.PropertyOrField(propertyExpr, member); - } - - var keySelector = Expression.Lambda>( - Expression.Convert(propertyExpr, typeof(object)), - paramExpr); - - ((List>)specificationBuilder.Specification.OrderExpressions) - .Add(new OrderExpressionInfo(keySelector, field.Value)); - } - } - - return new OrderedSpecificationBuilder(specificationBuilder.Specification); - } - - private static Dictionary ParseOrderBy(string[] orderByFields) => - new(orderByFields.Select((orderByfield, index) => - { - string[] fieldParts = orderByfield.Split(' '); - string field = fieldParts[0]; - bool descending = fieldParts.Length > 1 && fieldParts[1].StartsWith("Desc", StringComparison.OrdinalIgnoreCase); - var orderBy = index == 0 - ? descending ? OrderTypeEnum.OrderByDescending - : OrderTypeEnum.OrderBy - : descending ? OrderTypeEnum.ThenByDescending - : OrderTypeEnum.ThenBy; - - return new KeyValuePair(field, orderBy); - })); -} diff --git a/src/api/framework/Core/Storage/File/Features/FileUploadCommand.cs b/src/api/framework/Core/Storage/File/Features/FileUploadCommand.cs deleted file mode 100644 index c4e5cb0e53..0000000000 --- a/src/api/framework/Core/Storage/File/Features/FileUploadCommand.cs +++ /dev/null @@ -1,11 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Storage.File.Features; - -public class FileUploadCommand : IRequest -{ - public string Name { get; set; } = default!; - public string Extension { get; set; } = default!; - public string Data { get; set; } = default!; -} - diff --git a/src/api/framework/Core/Storage/File/Features/FileUploadResponse.cs b/src/api/framework/Core/Storage/File/Features/FileUploadResponse.cs deleted file mode 100644 index f3af35debf..0000000000 --- a/src/api/framework/Core/Storage/File/Features/FileUploadResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Core.Storage.File.Features; - -public class FileUploadResponse -{ - public Uri Url { get; set; } = default!; -} - diff --git a/src/api/framework/Core/Storage/File/Features/FileUploadValidator.cs b/src/api/framework/Core/Storage/File/Features/FileUploadValidator.cs deleted file mode 100644 index c064cf93f3..0000000000 --- a/src/api/framework/Core/Storage/File/Features/FileUploadValidator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Storage.File.Features; - -public class FileUploadRequestValidator : AbstractValidator -{ - public FileUploadRequestValidator() - { - RuleFor(p => p.Name) - .NotEmpty() - .MaximumLength(150); - - RuleFor(p => p.Extension) - .NotEmpty() - .MaximumLength(5); - - RuleFor(p => p.Data) - .NotEmpty(); - } -} - diff --git a/src/api/framework/Core/Storage/File/FileType.cs b/src/api/framework/Core/Storage/File/FileType.cs deleted file mode 100644 index 267968aaa6..0000000000 --- a/src/api/framework/Core/Storage/File/FileType.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.ComponentModel; - -namespace FSH.Framework.Core.Storage.File; - -public enum FileType -{ - [Description(".jpg,.png,.jpeg")] - Image -} diff --git a/src/api/framework/Core/Storage/IStorageService.cs b/src/api/framework/Core/Storage/IStorageService.cs deleted file mode 100644 index 5e13d6ddec..0000000000 --- a/src/api/framework/Core/Storage/IStorageService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Storage.File; -using FSH.Framework.Core.Storage.File.Features; - -namespace FSH.Framework.Core.Storage; - -public interface IStorageService -{ - public Task UploadAsync(FileUploadCommand? request, FileType supportedFileType, CancellationToken cancellationToken = default) - where T : class; - - public void Remove(Uri? path); -} diff --git a/src/api/framework/Core/Tenant/Abstractions/ITenantService.cs b/src/api/framework/Core/Tenant/Abstractions/ITenantService.cs deleted file mode 100644 index d2540a2ff3..0000000000 --- a/src/api/framework/Core/Tenant/Abstractions/ITenantService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FSH.Framework.Core.Tenant.Dtos; -using FSH.Framework.Core.Tenant.Features.CreateTenant; - -namespace FSH.Framework.Core.Tenant.Abstractions; - -public interface ITenantService -{ - Task> GetAllAsync(); - - Task ExistsWithIdAsync(string id); - - Task ExistsWithNameAsync(string name); - - Task GetByIdAsync(string id); - - Task CreateAsync(CreateTenantCommand request, CancellationToken cancellationToken); - - Task ActivateAsync(string id, CancellationToken cancellationToken); - - Task DeactivateAsync(string id); - - Task UpgradeSubscription(string id, DateTime extendedExpiryDate); -} diff --git a/src/api/framework/Core/Tenant/Dtos/TenantDetail.cs b/src/api/framework/Core/Tenant/Dtos/TenantDetail.cs deleted file mode 100644 index c9e44f8b7d..0000000000 --- a/src/api/framework/Core/Tenant/Dtos/TenantDetail.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace FSH.Framework.Core.Tenant.Dtos; -public class TenantDetail -{ - public string Id { get; set; } = default!; - public string Name { get; set; } = default!; - public string? ConnectionString { get; set; } - public string AdminEmail { get; set; } = default!; - public bool IsActive { get; set; } - public DateTime ValidUpto { get; set; } - public string? Issuer { get; set; } -} diff --git a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantCommand.cs b/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantCommand.cs deleted file mode 100644 index 01f902dfc9..0000000000 --- a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantCommand.cs +++ /dev/null @@ -1,4 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.ActivateTenant; -public record ActivateTenantCommand(string TenantId) : IRequest; diff --git a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantHandler.cs b/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantHandler.cs deleted file mode 100644 index ab018e532e..0000000000 --- a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.ActivateTenant; -public sealed class ActivateTenantHandler(ITenantService service) : IRequestHandler -{ - public async Task Handle(ActivateTenantCommand request, CancellationToken cancellationToken) - { - var status = await service.ActivateAsync(request.TenantId, cancellationToken); - return new ActivateTenantResponse(status); - } -} diff --git a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantResponse.cs b/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantResponse.cs deleted file mode 100644 index bd396891df..0000000000 --- a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Tenant.Features.ActivateTenant; -public record ActivateTenantResponse(string Status); diff --git a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantValidator.cs b/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantValidator.cs deleted file mode 100644 index de9bceb45e..0000000000 --- a/src/api/framework/Core/Tenant/Features/ActivateTenant/ActivateTenantValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Tenant.Features.ActivateTenant; -public sealed class ActivateTenantValidator : AbstractValidator -{ - public ActivateTenantValidator() => - RuleFor(t => t.TenantId) - .NotEmpty(); -} diff --git a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantCommand.cs b/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantCommand.cs deleted file mode 100644 index a6bec85931..0000000000 --- a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantCommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.CreateTenant; -public sealed record CreateTenantCommand(string Id, - string Name, - string? ConnectionString, - string AdminEmail, - string? Issuer) : IRequest; diff --git a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantHandler.cs b/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantHandler.cs deleted file mode 100644 index d948367cb8..0000000000 --- a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.CreateTenant; -public sealed class CreateTenantHandler(ITenantService service) : IRequestHandler -{ - public async Task Handle(CreateTenantCommand request, CancellationToken cancellationToken) - { - var tenantId = await service.CreateAsync(request, cancellationToken); - return new CreateTenantResponse(tenantId); - } -} diff --git a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantResponse.cs b/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantResponse.cs deleted file mode 100644 index 7a778e4f79..0000000000 --- a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Tenant.Features.CreateTenant; -public record CreateTenantResponse(string Id); diff --git a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantValidator.cs b/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantValidator.cs deleted file mode 100644 index 16e9816afb..0000000000 --- a/src/api/framework/Core/Tenant/Features/CreateTenant/CreateTenantValidator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FluentValidation; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Tenant.Abstractions; - -namespace FSH.Framework.Core.Tenant.Features.CreateTenant; -public class CreateTenantValidator : AbstractValidator -{ - public CreateTenantValidator( - ITenantService tenantService, - IConnectionStringValidator connectionStringValidator) - { - RuleFor(t => t.Id).Cascade(CascadeMode.Stop) - .NotEmpty() - .MustAsync(async (id, _) => !await tenantService.ExistsWithIdAsync(id).ConfigureAwait(false)) - .WithMessage((_, id) => $"Tenant {id} already exists."); - - RuleFor(t => t.Name).Cascade(CascadeMode.Stop) - .NotEmpty() - .MustAsync(async (name, _) => !await tenantService.ExistsWithNameAsync(name!).ConfigureAwait(false)) - .WithMessage((_, name) => $"Tenant {name} already exists."); - - RuleFor(t => t.ConnectionString).Cascade(CascadeMode.Stop) - .Must((_, cs) => string.IsNullOrWhiteSpace(cs) || connectionStringValidator.TryValidate(cs)) - .WithMessage("Connection string invalid."); - - RuleFor(t => t.AdminEmail).Cascade(CascadeMode.Stop) - .NotEmpty() - .EmailAddress(); - } -} diff --git a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantCommand.cs b/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantCommand.cs deleted file mode 100644 index bc0dc1fa95..0000000000 --- a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantCommand.cs +++ /dev/null @@ -1,4 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.DisableTenant; -public record DisableTenantCommand(string TenantId) : IRequest; diff --git a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantHandler.cs b/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantHandler.cs deleted file mode 100644 index d9cad8dcbd..0000000000 --- a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.DisableTenant; -public sealed class DisableTenantHandler(ITenantService service) : IRequestHandler -{ - public async Task Handle(DisableTenantCommand request, CancellationToken cancellationToken) - { - var status = await service.DeactivateAsync(request.TenantId); - return new DisableTenantResponse(status); - } -} diff --git a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantResponse.cs b/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantResponse.cs deleted file mode 100644 index 89ce0c0538..0000000000 --- a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Tenant.Features.DisableTenant; -public record DisableTenantResponse(string Status); diff --git a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantValidator.cs b/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantValidator.cs deleted file mode 100644 index 2c0831e209..0000000000 --- a/src/api/framework/Core/Tenant/Features/DisableTenant/DisableTenantValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Tenant.Features.DisableTenant; -public sealed class DisableTenantValidator : AbstractValidator -{ - public DisableTenantValidator() => - RuleFor(t => t.TenantId) - .NotEmpty(); -} diff --git a/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdHandler.cs b/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdHandler.cs deleted file mode 100644 index ec8e68737c..0000000000 --- a/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using FSH.Framework.Core.Tenant.Dtos; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.GetTenantById; -public sealed class GetTenantByIdHandler(ITenantService service) : IRequestHandler -{ - public async Task Handle(GetTenantByIdQuery request, CancellationToken cancellationToken) - { - return await service.GetByIdAsync(request.TenantId); - } -} diff --git a/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdQuery.cs b/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdQuery.cs deleted file mode 100644 index 9f75bc68c4..0000000000 --- a/src/api/framework/Core/Tenant/Features/GetTenantById/GetTenantByIdQuery.cs +++ /dev/null @@ -1,5 +0,0 @@ -using FSH.Framework.Core.Tenant.Dtos; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.GetTenantById; -public record GetTenantByIdQuery(string TenantId) : IRequest; diff --git a/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsHandler.cs b/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsHandler.cs deleted file mode 100644 index 1ccd5f90eb..0000000000 --- a/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using FSH.Framework.Core.Tenant.Dtos; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.GetTenants; -public sealed class GetTenantsHandler(ITenantService service) : IRequestHandler> -{ - public Task> Handle(GetTenantsQuery request, CancellationToken cancellationToken) - { - return service.GetAllAsync(); - } -} diff --git a/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsQuery.cs b/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsQuery.cs deleted file mode 100644 index dba6bc1896..0000000000 --- a/src/api/framework/Core/Tenant/Features/GetTenants/GetTenantsQuery.cs +++ /dev/null @@ -1,5 +0,0 @@ -using FSH.Framework.Core.Tenant.Dtos; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.GetTenants; -public sealed class GetTenantsQuery : IRequest>; diff --git a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionCommand.cs b/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionCommand.cs deleted file mode 100644 index f132f455b7..0000000000 --- a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionCommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.UpgradeSubscription; -public class UpgradeSubscriptionCommand : IRequest -{ - public string Tenant { get; set; } = default!; - public DateTime ExtendedExpiryDate { get; set; } -} diff --git a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionHandler.cs b/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionHandler.cs deleted file mode 100644 index e4cbbb4e7a..0000000000 --- a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FSH.Framework.Core.Tenant.Abstractions; -using MediatR; - -namespace FSH.Framework.Core.Tenant.Features.UpgradeSubscription; - -public class UpgradeSubscriptionHandler : IRequestHandler -{ - private readonly ITenantService _tenantService; - - public UpgradeSubscriptionHandler(ITenantService tenantService) => _tenantService = tenantService; - - public async Task Handle(UpgradeSubscriptionCommand request, CancellationToken cancellationToken) - { - var validUpto = await _tenantService.UpgradeSubscription(request.Tenant, request.ExtendedExpiryDate); - return new UpgradeSubscriptionResponse(validUpto, request.Tenant); - } -} diff --git a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionResponse.cs b/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionResponse.cs deleted file mode 100644 index ef14487b74..0000000000 --- a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Framework.Core.Tenant.Features.UpgradeSubscription; -public record UpgradeSubscriptionResponse(DateTime NewValidity, string Tenant); diff --git a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionValidator.cs b/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionValidator.cs deleted file mode 100644 index daddf1fbf1..0000000000 --- a/src/api/framework/Core/Tenant/Features/UpgradeSubscription/UpgradeSubscriptionValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Framework.Core.Tenant.Features.UpgradeSubscription; -public class UpgradeSubscriptionValidator : AbstractValidator -{ - public UpgradeSubscriptionValidator() - { - RuleFor(t => t.Tenant).NotEmpty(); - RuleFor(t => t.ExtendedExpiryDate).GreaterThan(DateTime.UtcNow); - } -} diff --git a/src/api/framework/Infrastructure/Auth/CurrentUserMiddleware.cs b/src/api/framework/Infrastructure/Auth/CurrentUserMiddleware.cs deleted file mode 100644 index b4deef0872..0000000000 --- a/src/api/framework/Infrastructure/Auth/CurrentUserMiddleware.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using Microsoft.AspNetCore.Http; - -namespace FSH.Framework.Infrastructure.Auth; -public class CurrentUserMiddleware(ICurrentUserInitializer currentUserInitializer) : IMiddleware -{ - private readonly ICurrentUserInitializer _currentUserInitializer = currentUserInitializer; - - public async Task InvokeAsync(HttpContext context, RequestDelegate next) - { - _currentUserInitializer.SetCurrentUser(context.User); - await next(context); - } -} diff --git a/src/api/framework/Infrastructure/Auth/Jwt/ConfigureJwtBearerOptions.cs b/src/api/framework/Infrastructure/Auth/Jwt/ConfigureJwtBearerOptions.cs deleted file mode 100644 index 8e495d207b..0000000000 --- a/src/api/framework/Infrastructure/Auth/Jwt/ConfigureJwtBearerOptions.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Security.Claims; -using System.Text; -using FSH.Framework.Core.Auth.Jwt; -using FSH.Framework.Core.Exceptions; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; - -namespace FSH.Framework.Infrastructure.Auth.Jwt; -public class ConfigureJwtBearerOptions : IConfigureNamedOptions -{ - private readonly JwtOptions _options; - - public ConfigureJwtBearerOptions(IOptions options) - { - _options = options.Value; - } - - public void Configure(JwtBearerOptions options) - { - Configure(string.Empty, options); - } - - public void Configure(string? name, JwtBearerOptions options) - { - if (name != JwtBearerDefaults.AuthenticationScheme) - { - return; - } - - byte[] key = Encoding.ASCII.GetBytes(_options.Key); - - options.RequireHttpsMetadata = false; - options.SaveToken = true; - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(key), - ValidIssuer = JwtAuthConstants.Issuer, - ValidateIssuer = true, - ValidateLifetime = true, - ValidAudience = JwtAuthConstants.Audience, - ValidateAudience = true, - RoleClaimType = ClaimTypes.Role, - ClockSkew = TimeSpan.Zero - }; - options.Events = new JwtBearerEvents - { - OnChallenge = context => - { - context.HandleResponse(); - if (!context.Response.HasStarted) - { - throw new UnauthorizedException(); - } - - return Task.CompletedTask; - }, - OnForbidden = _ => throw new ForbiddenException(), - OnMessageReceived = context => - { - var accessToken = context.Request.Query["access_token"]; - - if (!string.IsNullOrEmpty(accessToken) && - context.HttpContext.Request.Path.StartsWithSegments("/notifications", StringComparison.OrdinalIgnoreCase)) - { - // Read the token out of the query string - context.Token = accessToken; - } - - return Task.CompletedTask; - } - }; - } -} diff --git a/src/api/framework/Infrastructure/Auth/Jwt/Extensions.cs b/src/api/framework/Infrastructure/Auth/Jwt/Extensions.cs deleted file mode 100644 index 87a94d3ec1..0000000000 --- a/src/api/framework/Infrastructure/Auth/Jwt/Extensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using FSH.Framework.Core.Auth.Jwt; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Auth.Jwt; -internal static class Extensions -{ - internal static IServiceCollection ConfigureJwtAuth(this IServiceCollection services) - { - services.AddOptions() - .BindConfiguration(nameof(JwtOptions)) - .ValidateDataAnnotations() - .ValidateOnStart(); - - services.AddSingleton, ConfigureJwtBearerOptions>(); - services - .AddAuthentication(authentication => - { - authentication.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - authentication.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, null!); - - services.AddAuthorizationBuilder().AddRequiredPermissionPolicy(); - services.AddAuthorization(options => - { - options.FallbackPolicy = options.GetPolicy(RequiredPermissionDefaults.PolicyName); - }); - return services; - } -} diff --git a/src/api/framework/Infrastructure/Auth/Jwt/JwtAuthConstants.cs b/src/api/framework/Infrastructure/Auth/Jwt/JwtAuthConstants.cs deleted file mode 100644 index b766fdf804..0000000000 --- a/src/api/framework/Infrastructure/Auth/Jwt/JwtAuthConstants.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Infrastructure.Auth.Jwt; -internal static class JwtAuthConstants -{ - public const string Issuer = "https://fullstackhero.net"; - public const string Audience = "fullstackhero"; -} diff --git a/src/api/framework/Infrastructure/Auth/Policy/EndpointExtensions.cs b/src/api/framework/Infrastructure/Auth/Policy/EndpointExtensions.cs deleted file mode 100644 index 7cd6ea7e1e..0000000000 --- a/src/api/framework/Infrastructure/Auth/Policy/EndpointExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Builder; - -namespace FSH.Framework.Infrastructure.Auth.Policy; -public static class EndpointExtensions -{ - public static TBuilder RequirePermission( - this TBuilder endpointConventionBuilder, string requiredPermission, params string[] additionalRequiredPermissions) - where TBuilder : IEndpointConventionBuilder - { - return endpointConventionBuilder.WithMetadata(new RequiredPermissionAttribute(requiredPermission, additionalRequiredPermissions)); - } -} diff --git a/src/api/framework/Infrastructure/Auth/Policy/PermissionAuthorizationRequirement.cs b/src/api/framework/Infrastructure/Auth/Policy/PermissionAuthorizationRequirement.cs deleted file mode 100644 index 1286921818..0000000000 --- a/src/api/framework/Infrastructure/Auth/Policy/PermissionAuthorizationRequirement.cs +++ /dev/null @@ -1,4 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace FSH.Framework.Infrastructure.Auth.Policy; -public class PermissionAuthorizationRequirement : IAuthorizationRequirement; diff --git a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAttribute.cs b/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAttribute.cs deleted file mode 100644 index cf7ea3d91e..0000000000 --- a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace FSH.Framework.Infrastructure.Auth.Policy; -public interface IRequiredPermissionMetadata -{ - HashSet RequiredPermissions { get; } -} - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] -public sealed class RequiredPermissionAttribute(string? requiredPermission, params string[]? additionalRequiredPermissions) - : Attribute, IRequiredPermissionMetadata -{ - public HashSet RequiredPermissions { get; } = [requiredPermission!, .. additionalRequiredPermissions]; - public string? RequiredPermission { get; } - public string[]? AdditionalRequiredPermissions { get; } -} diff --git a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationExtensions.cs b/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationExtensions.cs deleted file mode 100644 index e92cdb2e68..0000000000 --- a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace FSH.Framework.Infrastructure.Auth.Policy; -public static class RequiredPermissionDefaults -{ - public const string PolicyName = "RequiredPermission"; -} - -public static class RequiredPermissionAuthorizationExtensions -{ - public static AuthorizationPolicyBuilder RequireRequiredPermissions(this AuthorizationPolicyBuilder builder) - { - return builder.AddRequirements(new PermissionAuthorizationRequirement()); - } - - public static AuthorizationBuilder AddRequiredPermissionPolicy(this AuthorizationBuilder builder) - { - builder.AddPolicy(RequiredPermissionDefaults.PolicyName, policy => - { - policy.RequireAuthenticatedUser(); - policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme); - policy.RequireRequiredPermissions(); - }); - - builder.Services.TryAddEnumerable(ServiceDescriptor.Scoped()); - - return builder; - } -} diff --git a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationHandler.cs b/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationHandler.cs deleted file mode 100644 index 8903b660e8..0000000000 --- a/src/api/framework/Infrastructure/Auth/Policy/RequiredPermissionAuthorizationHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; - -namespace FSH.Framework.Infrastructure.Auth.Policy; -public sealed class RequiredPermissionAuthorizationHandler(IUserService userService) : AuthorizationHandler -{ - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement) - { - var endpoint = context.Resource switch - { - HttpContext httpContext => httpContext.GetEndpoint(), - Endpoint ep => ep, - _ => null, - }; - - var requiredPermissions = endpoint?.Metadata.GetMetadata()?.RequiredPermissions; - if (requiredPermissions == null) - { - // there are no permission requirements set by the endpoint - // hence, authorize requests - context.Succeed(requirement); - return; - } - if (context.User?.GetUserId() is { } userId && await userService.HasPermissionAsync(userId, requiredPermissions.First())) - { - context.Succeed(requirement); - } - } -} diff --git a/src/api/framework/Infrastructure/Behaviours/ValidationBehavior.cs b/src/api/framework/Infrastructure/Behaviours/ValidationBehavior.cs deleted file mode 100644 index 016652aeb7..0000000000 --- a/src/api/framework/Infrastructure/Behaviours/ValidationBehavior.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; -using MediatR; - -namespace FSH.Framework.Infrastructure.Behaviours; -public class ValidationBehavior(IEnumerable> validators) : IPipelineBehavior - where TRequest : IRequest -{ - private readonly IEnumerable> _validators = validators; - - public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) - { - if (_validators.Any()) - { - var context = new ValidationContext(request); - var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); - var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); - - if (failures.Count > 0) - throw new ValidationException(failures); - } - return await next(); - } -} diff --git a/src/api/framework/Infrastructure/Caching/DistributedCacheService.cs b/src/api/framework/Infrastructure/Caching/DistributedCacheService.cs deleted file mode 100644 index f4353d69a5..0000000000 --- a/src/api/framework/Infrastructure/Caching/DistributedCacheService.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System.Text; -using System.Text.Json; -using FSH.Framework.Core.Caching; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Logging; - -namespace FSH.Framework.Infrastructure.Caching; - -public class DistributedCacheService : ICacheService -{ - private readonly IDistributedCache _cache; - private readonly ILogger _logger; - - public DistributedCacheService(IDistributedCache cache, ILogger logger) - { - (_cache, _logger) = (cache, logger); - } - - public T? Get(string key) => - Get(key) is { } data - ? Deserialize(data) - : default; - - private byte[]? Get(string key) - { - ArgumentNullException.ThrowIfNull(key); - - try - { - return _cache.Get(key); - } - catch - { - return null; - } - } - - public async Task GetAsync(string key, CancellationToken token = default) => - await GetAsync(key, token) is { } data - ? Deserialize(data) - : default; - - private async Task GetAsync(string key, CancellationToken token = default) - { - try - { - return await _cache.GetAsync(key, token); - } - catch (Exception ex) - { - Console.WriteLine(ex); - return null; - } - } - - public void Refresh(string key) - { - try - { - _cache.Refresh(key); - } - catch - { - // can be ignored - } - } - - public async Task RefreshAsync(string key, CancellationToken token = default) - { - try - { - await _cache.RefreshAsync(key, token); - _logger.LogDebug("refreshed cache with key : {Key}", key); - } - catch - { - // can be ignored - } - } - - public void Remove(string key) - { - try - { - _cache.Remove(key); - } - catch - { - // can be ignored - } - } - - public async Task RemoveAsync(string key, CancellationToken token = default) - { - try - { - await _cache.RemoveAsync(key, token); - } - catch - { - // can be ignored - } - } - - public void Set(string key, T value, TimeSpan? slidingExpiration = null) => - Set(key, Serialize(value), slidingExpiration); - - private void Set(string key, byte[] value, TimeSpan? slidingExpiration = null) - { - try - { - _cache.Set(key, value, GetOptions(slidingExpiration)); - _logger.LogDebug("cached data with key : {Key}", key); - } - catch - { - // can be ignored - } - } - - public Task SetAsync(string key, T value, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default) => - SetAsync(key, Serialize(value), slidingExpiration, cancellationToken); - - private async Task SetAsync(string key, byte[] value, TimeSpan? slidingExpiration = null, CancellationToken token = default) - { - try - { - await _cache.SetAsync(key, value, GetOptions(slidingExpiration), token); - _logger.LogDebug("cached data with key : {Key}", key); - } - catch - { - // can be ignored - } - } - - private static byte[] Serialize(T item) - { - return Encoding.Default.GetBytes(JsonSerializer.Serialize(item)); - } - - private static T Deserialize(byte[] cachedData) - { - return JsonSerializer.Deserialize(Encoding.Default.GetString(cachedData))!; - } - - private static DistributedCacheEntryOptions GetOptions(TimeSpan? slidingExpiration) - { - var options = new DistributedCacheEntryOptions(); - if (slidingExpiration.HasValue) - { - options.SetSlidingExpiration(slidingExpiration.Value); - } - else - { - options.SetSlidingExpiration(TimeSpan.FromMinutes(5)); - } - options.SetAbsoluteExpiration(TimeSpan.FromMinutes(15)); - return options; - } -} diff --git a/src/api/framework/Infrastructure/Caching/Extensions.cs b/src/api/framework/Infrastructure/Caching/Extensions.cs deleted file mode 100644 index c389a52a1b..0000000000 --- a/src/api/framework/Infrastructure/Caching/Extensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FSH.Framework.Core.Caching; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Serilog; - -namespace FSH.Framework.Infrastructure.Caching; -internal static class Extensions -{ - private static readonly ILogger _logger = Log.ForContext(typeof(Extensions)); - internal static IServiceCollection ConfigureCaching(this IServiceCollection services, IConfiguration configuration) - { - services.AddTransient(); - var cacheOptions = configuration.GetSection(nameof(CacheOptions)).Get(); - if (cacheOptions == null || string.IsNullOrEmpty(cacheOptions.Redis)) - { - _logger.Information("configuring memory cache."); - services.AddDistributedMemoryCache(); - return services; - } - - _logger.Information("configuring redis cache."); - services.AddStackExchangeRedisCache(options => - { - options.Configuration = cacheOptions.Redis; - options.ConfigurationOptions = new StackExchange.Redis.ConfigurationOptions() - { - AbortOnConnectFail = true, - EndPoints = { cacheOptions.Redis! } - }; - }); - - return services; - } -} diff --git a/src/api/framework/Infrastructure/Common/Extensions/EnumExtensions.cs b/src/api/framework/Infrastructure/Common/Extensions/EnumExtensions.cs deleted file mode 100644 index d1f3014aa3..0000000000 --- a/src/api/framework/Infrastructure/Common/Extensions/EnumExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Text.RegularExpressions; - -namespace FSH.Framework.Infrastructure.Common.Extensions; -public static class EnumExtensions -{ - public static string GetDescription(this Enum enumValue) - { - object[] attr = enumValue.GetType().GetField(enumValue.ToString())! - .GetCustomAttributes(typeof(DescriptionAttribute), false); - if (attr.Length > 0) - return ((DescriptionAttribute)attr[0]).Description; - string result = enumValue.ToString(); - result = Regex.Replace(result, "([a-z])([A-Z])", "$1 $2"); - result = Regex.Replace(result, "([A-Za-z])([0-9])", "$1 $2"); - result = Regex.Replace(result, "([0-9])([A-Za-z])", "$1 $2"); - result = Regex.Replace(result, "(? GetDescriptionList(this Enum enumValue) - { - string result = enumValue.GetDescription(); - return new ReadOnlyCollection(result.Split(',').ToList()); - } -} diff --git a/src/api/framework/Infrastructure/Common/Extensions/RegexExtensions.cs b/src/api/framework/Infrastructure/Common/Extensions/RegexExtensions.cs deleted file mode 100644 index 0204acd3c5..0000000000 --- a/src/api/framework/Infrastructure/Common/Extensions/RegexExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.RegularExpressions; - -namespace FSH.Framework.Infrastructure.Common.Extensions; -public static class RegexExtensions -{ - private static readonly Regex Whitespace = new(@"\s+"); - - public static string ReplaceWhitespace(this string input, string replacement) - { - return Whitespace.Replace(input, replacement); - } -} diff --git a/src/api/framework/Infrastructure/Constants/QueryStringKeys.cs b/src/api/framework/Infrastructure/Constants/QueryStringKeys.cs deleted file mode 100644 index 0ea2d30503..0000000000 --- a/src/api/framework/Infrastructure/Constants/QueryStringKeys.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Infrastructure.Constants; -public static class QueryStringKeys -{ - public const string Code = "code"; - public const string UserId = "userId"; -} diff --git a/src/api/framework/Infrastructure/Cors/CorsOptions.cs b/src/api/framework/Infrastructure/Cors/CorsOptions.cs deleted file mode 100644 index 8f2ef1b9eb..0000000000 --- a/src/api/framework/Infrastructure/Cors/CorsOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FSH.Framework.Infrastructure.Cors; -using System.Collections.ObjectModel; - -public class CorsOptions -{ - public CorsOptions() - { - AllowedOrigins = []; - } - - public Collection AllowedOrigins { get; } -} diff --git a/src/api/framework/Infrastructure/Cors/Extensions.cs b/src/api/framework/Infrastructure/Cors/Extensions.cs deleted file mode 100644 index d559de2067..0000000000 --- a/src/api/framework/Infrastructure/Cors/Extensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Framework.Infrastructure.Cors; -public static class Extensions -{ - private const string CorsPolicy = nameof(CorsPolicy); - internal static IServiceCollection AddCorsPolicy(this IServiceCollection services, IConfiguration config) - { - var corsOptions = config.GetSection(nameof(CorsOptions)).Get(); - if (corsOptions == null) { return services; } - return services.AddCors(opt => - opt.AddPolicy(CorsPolicy, policy => - policy.AllowAnyHeader() - .AllowAnyMethod() - .AllowCredentials() - .WithOrigins(corsOptions.AllowedOrigins.ToArray()))); - } - - internal static IApplicationBuilder UseCorsPolicy(this IApplicationBuilder app) - { - return app.UseCors(CorsPolicy); - } -} diff --git a/src/api/framework/Infrastructure/Exceptions/CustomExceptionHandler.cs b/src/api/framework/Infrastructure/Exceptions/CustomExceptionHandler.cs deleted file mode 100644 index c2d19308f2..0000000000 --- a/src/api/framework/Infrastructure/Exceptions/CustomExceptionHandler.cs +++ /dev/null @@ -1,51 +0,0 @@ -using FSH.Framework.Core.Exceptions; -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Serilog.Context; - -namespace FSH.Framework.Infrastructure.Exceptions; -public class CustomExceptionHandler(ILogger logger) : IExceptionHandler -{ - public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(httpContext); - ArgumentNullException.ThrowIfNull(exception); - var problemDetails = new ProblemDetails(); - problemDetails.Instance = httpContext.Request.Path; - - if (exception is FluentValidation.ValidationException fluentException) - { - problemDetails.Detail = "one or more validation errors occurred"; - problemDetails.Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"; - httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; - List validationErrors = new List(); - foreach (var error in fluentException.Errors) - { - validationErrors.Add(error.ErrorMessage); - } - problemDetails.Extensions.Add("errors", validationErrors); - } - - else if (exception is FshException e) - { - httpContext.Response.StatusCode = (int)e.StatusCode; - problemDetails.Detail = e.Message; - if (e.ErrorMessages != null && e.ErrorMessages.Any()) - { - problemDetails.Extensions.Add("errors", e.ErrorMessages); - } - } - - else - { - problemDetails.Detail = exception.Message; - } - - LogContext.PushProperty("StackTrace", exception.StackTrace); - logger.LogError("{ProblemDetail}", problemDetails.Detail); - await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken).ConfigureAwait(false); - return true; - } -} diff --git a/src/api/framework/Infrastructure/Extensions.cs b/src/api/framework/Infrastructure/Extensions.cs deleted file mode 100644 index 865bce172d..0000000000 --- a/src/api/framework/Infrastructure/Extensions.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Reflection; -using Asp.Versioning.Conventions; -using FluentValidation; -using FSH.Framework.Core; -using FSH.Framework.Core.Origin; -using FSH.Framework.Infrastructure.Auth; -using FSH.Framework.Infrastructure.Auth.Jwt; -using FSH.Framework.Infrastructure.Behaviours; -using FSH.Framework.Infrastructure.Caching; -using FSH.Framework.Infrastructure.Cors; -using FSH.Framework.Infrastructure.Exceptions; -using FSH.Framework.Infrastructure.Identity; -using FSH.Framework.Infrastructure.Jobs; -using FSH.Framework.Infrastructure.Logging.Serilog; -using FSH.Framework.Infrastructure.Mail; -using FSH.Framework.Infrastructure.OpenApi; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Framework.Infrastructure.RateLimit; -using FSH.Framework.Infrastructure.SecurityHeaders; -using FSH.Framework.Infrastructure.Storage.Files; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Framework.Infrastructure.Tenant.Endpoints; -using FSH.Starter.Aspire.ServiceDefaults; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; - -namespace FSH.Framework.Infrastructure; - -public static class Extensions -{ - public static WebApplicationBuilder ConfigureFshFramework(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - builder.AddServiceDefaults(); - builder.ConfigureSerilog(); - builder.ConfigureDatabase(); - builder.Services.ConfigureMultitenancy(); - builder.Services.ConfigureIdentity(); - builder.Services.AddCorsPolicy(builder.Configuration); - builder.Services.ConfigureFileStorage(); - builder.Services.ConfigureJwtAuth(); - builder.Services.ConfigureOpenApi(); - builder.Services.ConfigureJobs(builder.Configuration); - builder.Services.ConfigureMailing(); - builder.Services.ConfigureCaching(builder.Configuration); - builder.Services.AddExceptionHandler(); - builder.Services.AddProblemDetails(); - builder.Services.AddHealthChecks(); - builder.Services.AddOptions().BindConfiguration(nameof(OriginOptions)); - - // Define module assemblies - var assemblies = new Assembly[] - { - typeof(FshCore).Assembly, - typeof(FshInfrastructure).Assembly - }; - - // Register validators - builder.Services.AddValidatorsFromAssemblies(assemblies); - - // Register MediatR - builder.Services.AddMediatR(cfg => - { - cfg.RegisterServicesFromAssemblies(assemblies); - cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); - }); - - builder.Services.ConfigureRateLimit(builder.Configuration); - builder.Services.ConfigureSecurityHeaders(builder.Configuration); - - return builder; - } - - public static WebApplication UseFshFramework(this WebApplication app) - { - app.MapDefaultEndpoints(); - app.UseRateLimit(); - app.UseSecurityHeaders(); - app.UseMultitenancy(); - app.UseExceptionHandler(); - app.UseCorsPolicy(); - app.UseOpenApi(); - app.UseJobDashboard(app.Configuration); - app.UseRouting(); - app.UseStaticFiles(); - app.UseStaticFiles(new StaticFileOptions() - { - FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "assets")), - RequestPath = new PathString("/assets") - }); - app.UseAuthentication(); - app.UseAuthorization(); - app.MapTenantEndpoints(); - app.MapIdentityEndpoints(); - - // Current user middleware - app.UseMiddleware(); - - // Register API versions - var versions = app.NewApiVersionSet() - .HasApiVersion(1) - .HasApiVersion(2) - .ReportApiVersions() - .Build(); - - // Map versioned endpoint - app.MapGroup("api/v{version:apiVersion}").WithApiVersionSet(versions); - - return app; - } -} diff --git a/src/api/framework/Infrastructure/FshInfrastructure.cs b/src/api/framework/Infrastructure/FshInfrastructure.cs deleted file mode 100644 index d6b702c584..0000000000 --- a/src/api/framework/Infrastructure/FshInfrastructure.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Framework.Infrastructure; -public class FshInfrastructure -{ - public static string Name { get; set; } = "FshInfrastructure"; -} diff --git a/src/api/framework/Infrastructure/HealthChecks/HealthCheckEndpoint.cs b/src/api/framework/Infrastructure/HealthChecks/HealthCheckEndpoint.cs deleted file mode 100644 index 785aac77bb..0000000000 --- a/src/api/framework/Infrastructure/HealthChecks/HealthCheckEndpoint.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace FSH.Framework.Infrastructure.HealthChecks; -public static class HealthCheckEndpoint -{ - internal static RouteHandlerBuilder MapCustomHealthCheckEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/", (HttpContext context) => - { - var healthCheckService = context.RequestServices.GetRequiredService(); - var report = healthCheckService.CheckHealthAsync().Result; - - var response = new - { - status = report.Status.ToString(), - checks = report.Entries.Select(entry => new - { - name = entry.Key, - status = entry.Value.Status.ToString(), - description = entry.Value.Description - }), - - duration = report.TotalDuration - }; - - context.Response.ContentType = "application/json"; - return JsonSerializer.Serialize(response); - }) - .WithName("HealthCheck") - .WithSummary("Checks the health status of the application") - .WithDescription("Provides detailed health information about the application.") - .AllowAnonymous(); - } -} diff --git a/src/api/framework/Infrastructure/HealthChecks/HealthCheckMiddleware.cs b/src/api/framework/Infrastructure/HealthChecks/HealthCheckMiddleware.cs deleted file mode 100644 index 0311568702..0000000000 --- a/src/api/framework/Infrastructure/HealthChecks/HealthCheckMiddleware.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace FSH.Framework.Infrastructure.HealthChecks; - -public class HealthCheckMiddleware -{ - private readonly HealthCheckService _healthCheckService; - - public HealthCheckMiddleware(HealthCheckService healthCheckService) - { - _healthCheckService = healthCheckService; - } - - public async Task InvokeAsync(HttpContext context) - { - var report = await _healthCheckService.CheckHealthAsync(); - - var response = new - { - status = report.Status.ToString(), - checks = report.Entries.Select(entry => new - { - name = entry.Key, - status = entry.Value.Status.ToString(), - description = entry.Value.Description - }), - duration = report.TotalDuration - }; - - context.Response.ContentType = "application/json"; - await context.Response.WriteAsync(JsonSerializer.Serialize(response)); - } -} - diff --git a/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEvent.cs b/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEvent.cs deleted file mode 100644 index 46587882d7..0000000000 --- a/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEvent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.ObjectModel; -using FSH.Framework.Core.Audit; -using MediatR; - -namespace FSH.Framework.Infrastructure.Identity.Audit; -public class AuditPublishedEvent : INotification -{ - public AuditPublishedEvent(Collection? trails) - { - Trails = trails; - } - public Collection? Trails { get; } -} diff --git a/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEventHandler.cs b/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEventHandler.cs deleted file mode 100644 index cb255f82af..0000000000 --- a/src/api/framework/Infrastructure/Identity/Audit/AuditPublishedEventHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Core.Audit; -using FSH.Framework.Infrastructure.Identity.Persistence; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Framework.Infrastructure.Identity.Audit; -public class AuditPublishedEventHandler(ILogger logger, IdentityDbContext context) : INotificationHandler -{ - public async Task Handle(AuditPublishedEvent notification, CancellationToken cancellationToken) - { - if (context == null) return; - logger.LogInformation("received audit trails"); - try - { - await context.Set().AddRangeAsync(notification.Trails!, default); - await context.SaveChangesAsync(default); - } - catch - { - logger.LogError("error while saving audit trail"); - } - return; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Audit/AuditService.cs b/src/api/framework/Infrastructure/Identity/Audit/AuditService.cs deleted file mode 100644 index 823cb79576..0000000000 --- a/src/api/framework/Infrastructure/Identity/Audit/AuditService.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FSH.Framework.Core.Audit; -using FSH.Framework.Infrastructure.Identity.Persistence; -using Microsoft.EntityFrameworkCore; - -namespace FSH.Framework.Infrastructure.Identity.Audit; -public class AuditService(IdentityDbContext context) : IAuditService -{ - public async Task> GetUserTrailsAsync(Guid userId) - { - var trails = await context.AuditTrails - .Where(a => a.UserId == userId) - .OrderByDescending(a => a.DateTime) - .Take(250) - .ToListAsync(); - return trails; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Audit/Endpoints/GetUserAuditTrailEndpoint.cs b/src/api/framework/Infrastructure/Identity/Audit/Endpoints/GetUserAuditTrailEndpoint.cs deleted file mode 100644 index 78ca37942a..0000000000 --- a/src/api/framework/Infrastructure/Identity/Audit/Endpoints/GetUserAuditTrailEndpoint.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FSH.Framework.Core.Audit; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Audit.Endpoints; - -public static class GetUserAuditTrailEndpoint -{ - internal static RouteHandlerBuilder MapGetUserAuditTrailEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}/audit-trails", (Guid id, IAuditService service) => - { - return service.GetUserTrailsAsync(id); - }) - .WithName(nameof(GetUserAuditTrailEndpoint)) - .WithSummary("Get user's audit trail details") - .RequirePermission("Permissions.AuditTrails.View") - .WithDescription("Get user's audit trail details."); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Extensions.cs b/src/api/framework/Infrastructure/Identity/Extensions.cs deleted file mode 100644 index 4d20559295..0000000000 --- a/src/api/framework/Infrastructure/Identity/Extensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FSH.Framework.Core.Audit; -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Core.Identity.Tokens; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Auth; -using FSH.Framework.Infrastructure.Identity.Audit; -using FSH.Framework.Infrastructure.Identity.Persistence; -using FSH.Framework.Infrastructure.Identity.Roles; -using FSH.Framework.Infrastructure.Identity.Roles.Endpoints; -using FSH.Framework.Infrastructure.Identity.Tokens; -using FSH.Framework.Infrastructure.Identity.Tokens.Endpoints; -using FSH.Framework.Infrastructure.Identity.Users; -using FSH.Framework.Infrastructure.Identity.Users.Endpoints; -using FSH.Framework.Infrastructure.Identity.Users.Services; -using FSH.Framework.Infrastructure.Persistence; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using IdentityConstants = FSH.Starter.Shared.Authorization.IdentityConstants; - -namespace FSH.Framework.Infrastructure.Identity; -internal static class Extensions -{ - internal static IServiceCollection ConfigureIdentity(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(sp => (ICurrentUserInitializer)sp.GetRequiredService()); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.BindDbContext(); - services.AddScoped(); - services.AddIdentity(options => - { - options.Password.RequiredLength = IdentityConstants.PasswordLength; - options.Password.RequireDigit = false; - options.Password.RequireLowercase = false; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.User.RequireUniqueEmail = true; - }) - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - return services; - } - - public static IEndpointRouteBuilder MapIdentityEndpoints(this IEndpointRouteBuilder app) - { - var users = app.MapGroup("api/users").WithTags("users"); - users.MapUserEndpoints(); - - var tokens = app.MapGroup("api/token").WithTags("token"); - tokens.MapTokenEndpoints(); - - var roles = app.MapGroup("api/roles").WithTags("roles"); - roles.MapRoleEndpoints(); - - return app; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Persistence/IdentityConfiguration.cs b/src/api/framework/Infrastructure/Identity/Persistence/IdentityConfiguration.cs deleted file mode 100644 index 240b63fffc..0000000000 --- a/src/api/framework/Infrastructure/Identity/Persistence/IdentityConfiguration.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Finbuckle.MultiTenant; -using FSH.Framework.Core.Audit; -using FSH.Framework.Infrastructure.Identity.RoleClaims; -using FSH.Framework.Infrastructure.Identity.Roles; -using FSH.Framework.Infrastructure.Identity.Users; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using IdentityConstants = FSH.Starter.Shared.Authorization.IdentityConstants; - -namespace FSH.Framework.Infrastructure.Identity.Persistence; - -public class AuditTrailConfig : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder - .ToTable("AuditTrails", IdentityConstants.SchemaName) - .IsMultiTenant(); - - builder.HasKey(a => a.Id); - } -} - -public class ApplicationUserConfig : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder - .ToTable("Users", IdentityConstants.SchemaName) - .IsMultiTenant(); - - builder - .Property(u => u.ObjectId) - .HasMaxLength(256); - } -} - -public class ApplicationRoleConfig : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) => - builder - .ToTable("Roles", IdentityConstants.SchemaName) - .IsMultiTenant() - .AdjustUniqueIndexes(); -} - -public class ApplicationRoleClaimConfig : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) => - builder - .ToTable("RoleClaims", IdentityConstants.SchemaName) - .IsMultiTenant(); -} - -public class IdentityUserRoleConfig : IEntityTypeConfiguration> -{ - public void Configure(EntityTypeBuilder> builder) => - builder - .ToTable("UserRoles", IdentityConstants.SchemaName) - .IsMultiTenant(); -} - -public class IdentityUserClaimConfig : IEntityTypeConfiguration> -{ - public void Configure(EntityTypeBuilder> builder) => - builder - .ToTable("UserClaims", IdentityConstants.SchemaName) - .IsMultiTenant(); -} - -public class IdentityUserLoginConfig : IEntityTypeConfiguration> -{ - public void Configure(EntityTypeBuilder> builder) => - builder - .ToTable("UserLogins", IdentityConstants.SchemaName) - .IsMultiTenant(); -} - -public class IdentityUserTokenConfig : IEntityTypeConfiguration> -{ - public void Configure(EntityTypeBuilder> builder) => - builder - .ToTable("UserTokens", IdentityConstants.SchemaName) - .IsMultiTenant(); -} diff --git a/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbContext.cs b/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbContext.cs deleted file mode 100644 index 5945567c37..0000000000 --- a/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbContext.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using Finbuckle.MultiTenant.EntityFrameworkCore; -using FSH.Framework.Core.Audit; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Identity.RoleClaims; -using FSH.Framework.Infrastructure.Identity.Roles; -using FSH.Framework.Infrastructure.Identity.Users; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Framework.Infrastructure.Tenant; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Identity.Persistence; -public class IdentityDbContext : MultiTenantIdentityDbContext, - IdentityUserRole, - IdentityUserLogin, - FshRoleClaim, - IdentityUserToken> -{ - private readonly DatabaseOptions _settings; - private new FshTenantInfo TenantInfo { get; set; } - public IdentityDbContext(IMultiTenantContextAccessor multiTenantContextAccessor, DbContextOptions options, IOptions settings) : base(multiTenantContextAccessor, options) - { - _settings = settings.Value; - TenantInfo = multiTenantContextAccessor.MultiTenantContext.TenantInfo!; - } - - public DbSet AuditTrails { get; set; } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - builder.ApplyConfigurationsFromAssembly(typeof(IdentityDbContext).Assembly); - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (!string.IsNullOrWhiteSpace(TenantInfo?.ConnectionString)) - { - optionsBuilder.ConfigureDatabase(_settings.Provider, TenantInfo.ConnectionString); - } - } -} diff --git a/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbInitializer.cs b/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbInitializer.cs deleted file mode 100644 index f4759350cf..0000000000 --- a/src/api/framework/Infrastructure/Identity/Persistence/IdentityDbInitializer.cs +++ /dev/null @@ -1,136 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Origin; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Identity.RoleClaims; -using FSH.Framework.Infrastructure.Identity.Roles; -using FSH.Framework.Infrastructure.Identity.Users; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using IdentityConstants = FSH.Starter.Shared.Authorization.IdentityConstants; - -namespace FSH.Framework.Infrastructure.Identity.Persistence; -internal sealed class IdentityDbInitializer( - ILogger logger, - IdentityDbContext context, - RoleManager roleManager, - UserManager userManager, - TimeProvider timeProvider, - IMultiTenantContextAccessor multiTenantContextAccessor, - IOptions originSettings) : IDbInitializer -{ - public async Task MigrateAsync(CancellationToken cancellationToken) - { - if ((await context.Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any()) - { - await context.Database.MigrateAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("[{Tenant}] applied database migrations for identity module", context.TenantInfo?.Identifier); - } - } - - public async Task SeedAsync(CancellationToken cancellationToken) - { - await SeedRolesAsync(); - await SeedAdminUserAsync(); - } - - private async Task SeedRolesAsync() - { - foreach (string roleName in FshRoles.DefaultRoles) - { - if (await roleManager.Roles.SingleOrDefaultAsync(r => r.Name == roleName) - is not FshRole role) - { - // create role - role = new FshRole(roleName, $"{roleName} Role for {multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id} Tenant"); - await roleManager.CreateAsync(role); - } - - // Assign permissions - if (roleName == FshRoles.Basic) - { - await AssignPermissionsToRoleAsync(context, FshPermissions.Basic, role); - } - else if (roleName == FshRoles.Admin) - { - await AssignPermissionsToRoleAsync(context, FshPermissions.Admin, role); - - if (multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id == TenantConstants.Root.Id) - { - await AssignPermissionsToRoleAsync(context, FshPermissions.Root, role); - } - } - } - } - - private async Task AssignPermissionsToRoleAsync(IdentityDbContext dbContext, IReadOnlyList permissions, FshRole role) - { - var currentClaims = await roleManager.GetClaimsAsync(role); - var newClaims = permissions - .Where(permission => !currentClaims.Any(c => c.Type == FshClaims.Permission && c.Value == permission.Name)) - .Select(permission => new FshRoleClaim - { - RoleId = role.Id, - ClaimType = FshClaims.Permission, - ClaimValue = permission.Name, - CreatedBy = "application", - CreatedOn = timeProvider.GetUtcNow() - }) - .ToList(); - - foreach (var claim in newClaims) - { - logger.LogInformation("Seeding {Role} Permission '{Permission}' for '{TenantId}' Tenant.", role.Name, claim.ClaimValue, multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id); - await dbContext.RoleClaims.AddAsync(claim); - } - - // Save changes to the database context - if (newClaims.Count != 0) - { - await dbContext.SaveChangesAsync(); - } - - } - - private async Task SeedAdminUserAsync() - { - if (string.IsNullOrWhiteSpace(multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id) || string.IsNullOrWhiteSpace(multiTenantContextAccessor.MultiTenantContext.TenantInfo?.AdminEmail)) - { - return; - } - - if (await userManager.Users.FirstOrDefaultAsync(u => u.Email == multiTenantContextAccessor.MultiTenantContext.TenantInfo!.AdminEmail) - is not FshUser adminUser) - { - string adminUserName = $"{multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id.Trim()}.{FshRoles.Admin}".ToUpperInvariant(); - adminUser = new FshUser - { - FirstName = multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id.Trim().ToUpperInvariant(), - LastName = FshRoles.Admin, - Email = multiTenantContextAccessor.MultiTenantContext.TenantInfo?.AdminEmail, - UserName = adminUserName, - EmailConfirmed = true, - PhoneNumberConfirmed = true, - NormalizedEmail = multiTenantContextAccessor.MultiTenantContext.TenantInfo?.AdminEmail!.ToUpperInvariant(), - NormalizedUserName = adminUserName.ToUpperInvariant(), - ImageUrl = new Uri(originSettings.Value.OriginUrl! + TenantConstants.Root.DefaultProfilePicture), - IsActive = true - }; - - logger.LogInformation("Seeding Default Admin User for '{TenantId}' Tenant.", multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id); - var password = new PasswordHasher(); - adminUser.PasswordHash = password.HashPassword(adminUser, TenantConstants.DefaultPassword); - await userManager.CreateAsync(adminUser); - } - - // Assign role to user - if (!await userManager.IsInRoleAsync(adminUser, FshRoles.Admin)) - { - logger.LogInformation("Assigning Admin Role to Admin User for '{TenantId}' Tenant.", multiTenantContextAccessor.MultiTenantContext.TenantInfo?.Id); - await userManager.AddToRoleAsync(adminUser, FshRoles.Admin); - } - } -} diff --git a/src/api/framework/Infrastructure/Identity/RoleClaims/FshRoleClaim.cs b/src/api/framework/Infrastructure/Identity/RoleClaims/FshRoleClaim.cs deleted file mode 100644 index 210fa1bec6..0000000000 --- a/src/api/framework/Infrastructure/Identity/RoleClaims/FshRoleClaim.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace FSH.Framework.Infrastructure.Identity.RoleClaims; -public class FshRoleClaim : IdentityRoleClaim -{ - public string? CreatedBy { get; init; } - public DateTimeOffset CreatedOn { get; init; } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/CreateOrUpdateRoleEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/CreateOrUpdateRoleEndpoint.cs deleted file mode 100644 index 86234da7ec..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/CreateOrUpdateRoleEndpoint.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Core.Identity.Roles.Features.CreateOrUpdateRole; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; - -public static class CreateOrUpdateRoleEndpoint -{ - public static RouteHandlerBuilder MapCreateOrUpdateRoleEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/", async (CreateOrUpdateRoleCommand request, IRoleService roleService) => - { - return await roleService.CreateOrUpdateRoleAsync(request); - }) - .WithName(nameof(CreateOrUpdateRoleEndpoint)) - .WithSummary("Create or update a role") - .RequirePermission("Permissions.Roles.Create") - .WithDescription("Create a new role or update an existing role."); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/DeleteRoleEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/DeleteRoleEndpoint.cs deleted file mode 100644 index 106ea082bf..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/DeleteRoleEndpoint.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; - -public static class DeleteRoleEndpoint -{ - public static RouteHandlerBuilder MapDeleteRoleEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapDelete("/{id:guid}", async (string id, IRoleService roleService) => - { - await roleService.DeleteRoleAsync(id); - }) - .WithName(nameof(DeleteRoleEndpoint)) - .WithSummary("Delete a role by ID") - .RequirePermission("Permissions.Roles.Delete") - .WithDescription("Remove a role from the system by its ID."); - } -} - diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/Extensions.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/Extensions.cs deleted file mode 100644 index b899bb362c..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/Extensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; - -internal static class Extensions -{ - public static IEndpointRouteBuilder MapRoleEndpoints(this IEndpointRouteBuilder app) - { - app.MapGetRoleEndpoint(); - app.MapGetRolesEndpoint(); - app.MapDeleteRoleEndpoint(); - app.MapCreateOrUpdateRoleEndpoint(); - app.MapGetRolePermissionsEndpoint(); - app.MapUpdateRolePermissionsEndpoint(); - return app; - } -} - diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRoleEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRoleEndpoint.cs deleted file mode 100644 index 6064a33866..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRoleEndpoint.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; - -public static class GetRoleByIdEndpoint -{ - public static RouteHandlerBuilder MapGetRoleEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}", async (string id, IRoleService roleService) => - { - return await roleService.GetRoleAsync(id); - }) - .WithName(nameof(GetRoleByIdEndpoint)) - .WithSummary("Get role details by ID") - .RequirePermission("Permissions.Roles.View") - .WithDescription("Retrieve the details of a role by its ID."); - } -} - diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolePermissionsEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolePermissionsEndpoint.cs deleted file mode 100644 index 42eb894263..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolePermissionsEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; -public static class GetRolePermissionsEndpoint -{ - public static RouteHandlerBuilder MapGetRolePermissionsEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}/permissions", async (string id, IRoleService roleService, CancellationToken cancellationToken) => - { - return await roleService.GetWithPermissionsAsync(id, cancellationToken); - }) - .WithName(nameof(GetRolePermissionsEndpoint)) - .WithSummary("get role permissions") - .RequirePermission("Permissions.Roles.View") - .WithDescription("get role permissions"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolesEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolesEndpoint.cs deleted file mode 100644 index df3b91cff8..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/GetRolesEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; -public static class GetRolesEndpoint -{ - public static RouteHandlerBuilder MapGetRolesEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/", async (IRoleService roleService) => - { - return await roleService.GetRolesAsync(); - }) - .WithName(nameof(GetRolesEndpoint)) - .WithSummary("Get a list of all roles") - .RequirePermission("Permissions.Roles.View") - .WithDescription("Retrieve a list of all roles available in the system."); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/UpdateRolePermissionsEndpoint.cs b/src/api/framework/Infrastructure/Identity/Roles/Endpoints/UpdateRolePermissionsEndpoint.cs deleted file mode 100644 index 71cb44c611..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/Endpoints/UpdateRolePermissionsEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FluentValidation; -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Core.Identity.Roles.Features.UpdatePermissions; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Roles.Endpoints; -public static class UpdateRolePermissionsEndpoint -{ - public static RouteHandlerBuilder MapUpdateRolePermissionsEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPut("/{id}/permissions", async ( - UpdatePermissionsCommand request, - IRoleService roleService, - string id, - [FromServices] IValidator validator) => - { - if (id != request.RoleId) return Results.BadRequest(); - var response = await roleService.UpdatePermissionsAsync(request); - return Results.Ok(response); - }) - .WithName(nameof(UpdateRolePermissionsEndpoint)) - .WithSummary("update role permissions") - .RequirePermission("Permissions.Roles.Create") - .WithDescription("update role permissions"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/FshRole.cs b/src/api/framework/Infrastructure/Identity/Roles/FshRole.cs deleted file mode 100644 index fa8baf6bc6..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/FshRole.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace FSH.Framework.Infrastructure.Identity.Roles; -public class FshRole : IdentityRole -{ - public string? Description { get; set; } - - public FshRole(string name, string? description = null) - : base(name) - { - Description = description; - NormalizedName = name.ToUpperInvariant(); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Roles/RoleService.cs b/src/api/framework/Infrastructure/Identity/Roles/RoleService.cs deleted file mode 100644 index 6930ab0e16..0000000000 --- a/src/api/framework/Infrastructure/Identity/Roles/RoleService.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Roles; -using FSH.Framework.Core.Identity.Roles.Features.CreateOrUpdateRole; -using FSH.Framework.Core.Identity.Roles.Features.UpdatePermissions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Identity.Persistence; -using FSH.Framework.Infrastructure.Identity.RoleClaims; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; - -namespace FSH.Framework.Infrastructure.Identity.Roles; - -public class RoleService(RoleManager roleManager, - IdentityDbContext context, - IMultiTenantContextAccessor multiTenantContextAccessor, - ICurrentUser currentUser) : IRoleService -{ - private readonly RoleManager _roleManager = roleManager; - - public async Task> GetRolesAsync() - { - return await Task.Run(() => _roleManager.Roles - .Select(role => new RoleDto { Id = role.Id, Name = role.Name!, Description = role.Description }) - .ToList()); - } - - public async Task GetRoleAsync(string id) - { - FshRole? role = await _roleManager.FindByIdAsync(id); - - _ = role ?? throw new NotFoundException("role not found"); - - return new RoleDto { Id = role.Id, Name = role.Name!, Description = role.Description }; - } - - public async Task CreateOrUpdateRoleAsync(CreateOrUpdateRoleCommand command) - { - FshRole? role = await _roleManager.FindByIdAsync(command.Id); - - if (role != null) - { - role.Name = command.Name; - role.Description = command.Description; - await _roleManager.UpdateAsync(role); - } - else - { - role = new FshRole(command.Name, command.Description); - await _roleManager.CreateAsync(role); - } - - return new RoleDto { Id = role.Id, Name = role.Name!, Description = role.Description }; - } - - public async Task DeleteRoleAsync(string id) - { - FshRole? role = await _roleManager.FindByIdAsync(id); - - _ = role ?? throw new NotFoundException("role not found"); - - await _roleManager.DeleteAsync(role); - } - - public async Task GetWithPermissionsAsync(string id, CancellationToken cancellationToken) - { - var role = await GetRoleAsync(id); - _ = role ?? throw new NotFoundException("role not found"); - - role.Permissions = await context.RoleClaims - .Where(c => c.RoleId == id && c.ClaimType == FshClaims.Permission) - .Select(c => c.ClaimValue!) - .ToListAsync(cancellationToken); - - return role; - } - - public async Task UpdatePermissionsAsync(UpdatePermissionsCommand request) - { - var role = await _roleManager.FindByIdAsync(request.RoleId); - _ = role ?? throw new NotFoundException("role not found"); - if (role.Name == FshRoles.Admin) - { - throw new FshException("operation not permitted"); - } - - if (multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id != TenantConstants.Root.Id) - { - // Remove Root Permissions if the Role is not created for Root Tenant. - request.Permissions.RemoveAll(u => u.StartsWith("Permissions.Root.", StringComparison.InvariantCultureIgnoreCase)); - } - - var currentClaims = await _roleManager.GetClaimsAsync(role); - - // Remove permissions that were previously selected - foreach (var claim in currentClaims.Where(c => !request.Permissions.Exists(p => p == c.Value))) - { - var result = await _roleManager.RemoveClaimAsync(role, claim); - if (!result.Succeeded) - { - var errors = result.Errors.Select(error => error.Description).ToList(); - throw new FshException("operation failed", errors); - } - } - - // Add all permissions that were not previously selected - foreach (string permission in request.Permissions.Where(c => !currentClaims.Any(p => p.Value == c))) - { - if (!string.IsNullOrEmpty(permission)) - { - context.RoleClaims.Add(new FshRoleClaim - { - RoleId = role.Id, - ClaimType = FshClaims.Permission, - ClaimValue = permission, - CreatedBy = currentUser.GetUserId().ToString() - }); - await context.SaveChangesAsync(); - } - } - - return "permissions updated"; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/Extensions.cs b/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/Extensions.cs deleted file mode 100644 index 3bd70a42c4..0000000000 --- a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/Extensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Tokens.Endpoints; -internal static class Extensions -{ - public static IEndpointRouteBuilder MapTokenEndpoints(this IEndpointRouteBuilder app) - { - app.MapRefreshTokenEndpoint(); - app.MapTokenGenerationEndpoint(); - return app; - } - - public static string GetIpAddress(this HttpContext context) - { - string ip = "N/A"; - if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var ipList)) - { - ip = ipList.FirstOrDefault() ?? "N/A"; - } - else if (context.Connection.RemoteIpAddress != null) - { - ip = context.Connection.RemoteIpAddress.MapToIPv4().ToString(); - } - return ip; - - } -} diff --git a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/RefreshTokenEndpoint.cs b/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/RefreshTokenEndpoint.cs deleted file mode 100644 index a8f27128ba..0000000000 --- a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/RefreshTokenEndpoint.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FSH.Framework.Core.Identity.Tokens; -using FSH.Framework.Core.Identity.Tokens.Features.Refresh; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Tokens.Endpoints; -public static class RefreshTokenEndpoint -{ - internal static RouteHandlerBuilder MapRefreshTokenEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/refresh", (RefreshTokenCommand request, - [FromHeader(Name = TenantConstants.Identifier)] string tenant, - ITokenService service, - HttpContext context, - CancellationToken cancellationToken) => - { - string ip = context.GetIpAddress(); - return service.RefreshTokenAsync(request, ip!, cancellationToken); - }) - .WithName(nameof(RefreshTokenEndpoint)) - .WithSummary("refresh JWTs") - .WithDescription("refresh JWTs") - .AllowAnonymous(); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/TokenGenerationEndpoint.cs b/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/TokenGenerationEndpoint.cs deleted file mode 100644 index e0bbc4796e..0000000000 --- a/src/api/framework/Infrastructure/Identity/Tokens/Endpoints/TokenGenerationEndpoint.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FSH.Framework.Core.Identity.Tokens; -using FSH.Framework.Core.Identity.Tokens.Features.Generate; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Tokens.Endpoints; -public static class TokenGenerationEndpoint -{ - internal static RouteHandlerBuilder MapTokenGenerationEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/", (TokenGenerationCommand request, - [FromHeader(Name = TenantConstants.Identifier)] string tenant, - ITokenService service, - HttpContext context, - CancellationToken cancellationToken) => - { - string ip = context.GetIpAddress(); - return service.GenerateTokenAsync(request, ip!, cancellationToken); - }) - .WithName(nameof(TokenGenerationEndpoint)) - .WithSummary("generate JWTs") - .WithDescription("generate JWTs") - .AllowAnonymous(); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Tokens/TokenService.cs b/src/api/framework/Infrastructure/Identity/Tokens/TokenService.cs deleted file mode 100644 index 4f0bc60145..0000000000 --- a/src/api/framework/Infrastructure/Identity/Tokens/TokenService.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Security.Cryptography; -using System.Text; -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Auth.Jwt; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Tokens; -using FSH.Framework.Core.Identity.Tokens.Features.Generate; -using FSH.Framework.Core.Identity.Tokens.Features.Refresh; -using FSH.Framework.Core.Identity.Tokens.Models; -using FSH.Framework.Infrastructure.Auth.Jwt; -using FSH.Framework.Infrastructure.Identity.Audit; -using FSH.Framework.Infrastructure.Identity.Users; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.Shared.Authorization; -using MediatR; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; - -namespace FSH.Framework.Infrastructure.Identity.Tokens; -public sealed class TokenService : ITokenService -{ - private readonly UserManager _userManager; - private readonly IMultiTenantContextAccessor? _multiTenantContextAccessor; - private readonly JwtOptions _jwtOptions; - private readonly IPublisher _publisher; - public TokenService(IOptions jwtOptions, UserManager userManager, IMultiTenantContextAccessor? multiTenantContextAccessor, IPublisher publisher) - { - _jwtOptions = jwtOptions.Value; - _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); - _multiTenantContextAccessor = multiTenantContextAccessor; - _publisher = publisher; - } - - public async Task GenerateTokenAsync(TokenGenerationCommand request, string ipAddress, CancellationToken cancellationToken) - { - var currentTenant = _multiTenantContextAccessor!.MultiTenantContext.TenantInfo; - if (currentTenant == null) throw new UnauthorizedException(); - if (string.IsNullOrWhiteSpace(currentTenant.Id) - || await _userManager.FindByEmailAsync(request.Email.Trim().Normalize()) is not { } user - || !await _userManager.CheckPasswordAsync(user, request.Password)) - { - throw new UnauthorizedException(); - } - - if (!user.IsActive) - { - throw new UnauthorizedException("user is deactivated"); - } - - if (!user.EmailConfirmed) - { - throw new UnauthorizedException("email not confirmed"); - } - - if (currentTenant.Id != TenantConstants.Root.Id) - { - if (!currentTenant.IsActive) - { - throw new UnauthorizedException($"tenant {currentTenant.Id} is deactivated"); - } - - if (DateTime.UtcNow > currentTenant.ValidUpto) - { - throw new UnauthorizedException($"tenant {currentTenant.Id} validity has expired"); - } - } - - return await GenerateTokensAndUpdateUser(user, ipAddress); - } - - - public async Task RefreshTokenAsync(RefreshTokenCommand request, string ipAddress, CancellationToken cancellationToken) - { - var userPrincipal = GetPrincipalFromExpiredToken(request.Token); - var userId = _userManager.GetUserId(userPrincipal)!; - var user = await _userManager.FindByIdAsync(userId); - if (user is null) - { - throw new UnauthorizedException(); - } - - if (user.RefreshToken != request.RefreshToken || user.RefreshTokenExpiryTime <= DateTime.UtcNow) - { - throw new UnauthorizedException("Invalid Refresh Token"); - } - - return await GenerateTokensAndUpdateUser(user, ipAddress); - } - private async Task GenerateTokensAndUpdateUser(FshUser user, string ipAddress) - { - string token = GenerateJwt(user, ipAddress); - - user.RefreshToken = GenerateRefreshToken(); - user.RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(_jwtOptions.RefreshTokenExpirationInDays); - - await _userManager.UpdateAsync(user); - - await _publisher.Publish(new AuditPublishedEvent(new() - { - new() - { - Id = Guid.NewGuid(), - Operation = "Token Generated", - Entity = "Identity", - UserId = new Guid(user.Id), - DateTime = DateTime.UtcNow, - } - })); - - return new TokenResponse(token, user.RefreshToken, user.RefreshTokenExpiryTime); - } - - private string GenerateJwt(FshUser user, string ipAddress) => - GenerateEncryptedToken(GetSigningCredentials(), GetClaims(user, ipAddress)); - - private SigningCredentials GetSigningCredentials() - { - byte[] secret = Encoding.UTF8.GetBytes(_jwtOptions.Key); - return new SigningCredentials(new SymmetricSecurityKey(secret), SecurityAlgorithms.HmacSha256); - } - - private string GenerateEncryptedToken(SigningCredentials signingCredentials, IEnumerable claims) - { - var token = new JwtSecurityToken( - claims: claims, - expires: DateTime.UtcNow.AddMinutes(_jwtOptions.TokenExpirationInMinutes), - signingCredentials: signingCredentials, - issuer: JwtAuthConstants.Issuer, - audience: JwtAuthConstants.Audience - ); - var tokenHandler = new JwtSecurityTokenHandler(); - return tokenHandler.WriteToken(token); - } - - private List GetClaims(FshUser user, string ipAddress) => - new List - { - new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), - new(ClaimTypes.NameIdentifier, user.Id), - new(ClaimTypes.Email, user.Email!), - new(ClaimTypes.Name, user.FirstName ?? string.Empty), - new(ClaimTypes.MobilePhone, user.PhoneNumber ?? string.Empty), - new(FshClaims.Fullname, $"{user.FirstName} {user.LastName}"), - new(ClaimTypes.Surname, user.LastName ?? string.Empty), - new(FshClaims.IpAddress, ipAddress), - new(FshClaims.Tenant, _multiTenantContextAccessor!.MultiTenantContext.TenantInfo!.Id), - new(FshClaims.ImageUrl, user.ImageUrl == null ? string.Empty : user.ImageUrl.ToString()) - }; - private static string GenerateRefreshToken() - { - byte[] randomNumber = new byte[32]; - using var rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomNumber); - return Convert.ToBase64String(randomNumber); - } - - private ClaimsPrincipal GetPrincipalFromExpiredToken(string token) - { -#pragma warning disable CA5404 // Do not disable token validation checks - var tokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Key)), - ValidateIssuer = true, - ValidateAudience = true, - ValidAudience = JwtAuthConstants.Audience, - ValidIssuer = JwtAuthConstants.Issuer, - RoleClaimType = ClaimTypes.Role, - ClockSkew = TimeSpan.Zero, - ValidateLifetime = false - }; -#pragma warning restore CA5404 // Do not disable token validation checks - var tokenHandler = new JwtSecurityTokenHandler(); - var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken); - if (securityToken is not JwtSecurityToken jwtSecurityToken || - !jwtSecurityToken.Header.Alg.Equals( - SecurityAlgorithms.HmacSha256, - StringComparison.OrdinalIgnoreCase)) - { - throw new UnauthorizedException("invalid token"); - } - - return principal; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/AssignRolesToUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/AssignRolesToUserEndpoint.cs deleted file mode 100644 index 161fe18e8f..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/AssignRolesToUserEndpoint.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FluentValidation; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.AssignUserRole; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class AssignRolesToUserEndpoint -{ - internal static RouteHandlerBuilder MapAssignRolesToUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/{id:guid}/roles", async (AssignUserRoleCommand command, - HttpContext context, - string id, - IUserService userService, - CancellationToken cancellationToken) => - { - - var message = await userService.AssignRolesAsync(id, command, cancellationToken); - return Results.Ok(message); - }) - .WithName(nameof(AssignRolesToUserEndpoint)) - .WithSummary("assign roles") - .WithDescription("assign roles"); - } - -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ChangePasswordEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/ChangePasswordEndpoint.cs deleted file mode 100644 index 7164ac5668..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ChangePasswordEndpoint.cs +++ /dev/null @@ -1,43 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.ChangePassword; -using FSH.Framework.Core.Origin; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class ChangePasswordEndpoint -{ - internal static RouteHandlerBuilder MapChangePasswordEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/change-password", async (ChangePasswordCommand command, - HttpContext context, - IOptions settings, - IValidator validator, - IUserService userService, - CancellationToken cancellationToken) => - { - ValidationResult result = await validator.ValidateAsync(command, cancellationToken); - if (!result.IsValid) - { - return Results.ValidationProblem(result.ToDictionary()); - } - - if (context.User.GetUserId() is not { } userId || string.IsNullOrEmpty(userId)) - { - return Results.BadRequest(); - } - - await userService.ChangePasswordAsync(command, userId); - return Results.Ok("password reset email sent"); - }) - .WithName(nameof(ChangePasswordEndpoint)) - .WithSummary("Changes password") - .WithDescription("Change password"); - } - -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ConfirmEmailEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/ConfirmEmailEndpoint.cs deleted file mode 100644 index c0800e3321..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ConfirmEmailEndpoint.cs +++ /dev/null @@ -1,20 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class ConfirmEmailEndpoint -{ - internal static RouteHandlerBuilder MapConfirmEmailEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/confirm-email", (string userId, string code, string tenant, IUserService service) => - { - return service.ConfirmEmailAsync(userId, code, tenant, default); - }) - .WithName(nameof(ConfirmEmailEndpoint)) - .WithSummary("confirm user email") - .WithDescription("confirm user email") - .AllowAnonymous(); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/DeleteUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/DeleteUserEndpoint.cs deleted file mode 100644 index 6969b4e124..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/DeleteUserEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class DeleteUserEndpoint -{ - internal static RouteHandlerBuilder MapDeleteUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapDelete("/{id:guid}", (string id, IUserService service) => - { - return service.DeleteAsync(id); - }) - .WithName(nameof(DeleteUserEndpoint)) - .WithSummary("delete user profile") - .RequirePermission("Permissions.Users.Delete") - .WithDescription("delete user profile"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/Extensions.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/Extensions.cs deleted file mode 100644 index cbc311c6be..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/Extensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FSH.Framework.Infrastructure.Identity.Audit.Endpoints; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -internal static class Extensions -{ - public static IEndpointRouteBuilder MapUserEndpoints(this IEndpointRouteBuilder app) - { - app.MapRegisterUserEndpoint(); - app.MapSelfRegisterUserEndpoint(); - app.MapUpdateUserEndpoint(); - app.MapGetUsersListEndpoint(); - app.MapDeleteUserEndpoint(); - app.MapForgotPasswordEndpoint(); - app.MapChangePasswordEndpoint(); - app.MapResetPasswordEndpoint(); - app.MapGetMeEndpoint(); - app.MapGetUserEndpoint(); - app.MapGetCurrentUserPermissionsEndpoint(); - app.ToggleUserStatusEndpointEndpoint(); - app.MapAssignRolesToUserEndpoint(); - app.MapGetUserRolesEndpoint(); - app.MapGetUserAuditTrailEndpoint(); - app.MapConfirmEmailEndpoint(); - return app; - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ForgotPasswordEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/ForgotPasswordEndpoint.cs deleted file mode 100644 index 9483571e7a..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ForgotPasswordEndpoint.cs +++ /dev/null @@ -1,45 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.ForgotPassword; -using FSH.Framework.Core.Origin; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; - -public static class ForgotPasswordEndpoint -{ - internal static RouteHandlerBuilder MapForgotPasswordEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/forgot-password", async (HttpRequest request, [FromHeader(Name = TenantConstants.Identifier)] string tenant, ForgotPasswordCommand command, IOptions settings, IValidator validator, IUserService userService, CancellationToken cancellationToken) => - { - ValidationResult result = await validator.ValidateAsync(command, cancellationToken); - if (!result.IsValid) - { - return Results.ValidationProblem(result.ToDictionary()); - } - - // Obtain origin from appsettings - var origin = settings.Value; - - if (origin?.OriginUrl == null) - { - // Handle the case where OriginUrl is null - return Results.BadRequest("Origin URL is not configured."); - } - - await userService.ForgotPasswordAsync(command, origin.OriginUrl.ToString(), cancellationToken); - return Results.Ok("Password reset email sent."); - }) - .WithName(nameof(ForgotPasswordEndpoint)) - .WithSummary("Forgot password") - .WithDescription("Generates a password reset token and sends it via email.") - .AllowAnonymous(); - } - -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserEndpoint.cs deleted file mode 100644 index c23a8f5f40..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class GetUserEndpoint -{ - internal static RouteHandlerBuilder MapGetUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}", (string id, IUserService service) => - { - return service.GetAsync(id, CancellationToken.None); - }) - .WithName(nameof(GetUserEndpoint)) - .WithSummary("Get user profile by ID") - .RequirePermission("Permissions.Users.View") - .WithDescription("Get another user's profile details by user ID."); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserPermissionsEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserPermissionsEndpoint.cs deleted file mode 100644 index 6ee0f74eee..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserPermissionsEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Security.Claims; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class GetUserPermissionsEndpoint -{ - internal static RouteHandlerBuilder MapGetCurrentUserPermissionsEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/permissions", async (ClaimsPrincipal user, IUserService service, CancellationToken cancellationToken) => - { - if (user.GetUserId() is not { } userId || string.IsNullOrEmpty(userId)) - { - throw new UnauthorizedException(); - } - - return await service.GetPermissionsAsync(userId, cancellationToken); - }) - .WithName("GetUserPermissions") - .WithSummary("Get current user permissions") - .WithDescription("Get current user permissions"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserProfileEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserProfileEndpoint.cs deleted file mode 100644 index 9f3ea36ab4..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserProfileEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FSH.Framework.Core.Exceptions; -using System.Security.Claims; -using FSH.Framework.Core.Identity.Users.Abstractions; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using FSH.Starter.Shared.Authorization; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class GetUserProfileEndpoint -{ - internal static RouteHandlerBuilder MapGetMeEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/profile", async (ClaimsPrincipal user, IUserService service, CancellationToken cancellationToken) => - { - if (user.GetUserId() is not { } userId || string.IsNullOrEmpty(userId)) - { - throw new UnauthorizedException(); - } - - return await service.GetAsync(userId, cancellationToken); - }) - .WithName("GetMeEndpoint") - .WithSummary("Get current user information based on token") - .WithDescription("Get current user information based on token"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserRolesEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserRolesEndpoint.cs deleted file mode 100644 index 757f842926..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUserRolesEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class GetUserRolesEndpoint -{ - internal static RouteHandlerBuilder MapGetUserRolesEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}/roles", (string id, IUserService service) => - { - return service.GetUserRolesAsync(id, CancellationToken.None); - }) - .WithName(nameof(GetUserRolesEndpoint)) - .WithSummary("get user roles") - .RequirePermission("Permissions.Users.View") - .WithDescription("get user roles"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUsersListEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUsersListEndpoint.cs deleted file mode 100644 index 0743634ca8..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/GetUsersListEndpoint.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class GetUsersListEndpoint -{ - internal static RouteHandlerBuilder MapGetUsersListEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/", (CancellationToken cancellationToken, IUserService service) => - { - return service.GetListAsync(cancellationToken); - }) - .WithName(nameof(GetUsersListEndpoint)) - .WithSummary("get users list") - .RequirePermission("Permissions.Users.View") - .WithDescription("get users list"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/RegisterUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/RegisterUserEndpoint.cs deleted file mode 100644 index 84b98a911f..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/RegisterUserEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.RegisterUser; -using FSH.Framework.Infrastructure.Auth.Policy; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class RegisterUserEndpoint -{ - internal static RouteHandlerBuilder MapRegisterUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/register", (RegisterUserCommand request, - IUserService service, - HttpContext context, - CancellationToken cancellationToken) => - { - var origin = $"{context.Request.Scheme}://{context.Request.Host.Value}{context.Request.PathBase.Value}"; - return service.RegisterAsync(request, origin, cancellationToken); - }) - .WithName(nameof(RegisterUserEndpoint)) - .WithSummary("register user") - .RequirePermission("Permissions.Users.Create") - .WithDescription("register user"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ResetPasswordEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/ResetPasswordEndpoint.cs deleted file mode 100644 index a1f7187208..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ResetPasswordEndpoint.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.ResetPassword; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; - -public static class ResetPasswordEndpoint -{ - internal static RouteHandlerBuilder MapResetPasswordEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/reset-password", async (ResetPasswordCommand command, [FromHeader(Name = TenantConstants.Identifier)] string tenant, IValidator validator, IUserService userService, CancellationToken cancellationToken) => - { - ValidationResult result = await validator.ValidateAsync(command, cancellationToken); - if (!result.IsValid) - { - return Results.ValidationProblem(result.ToDictionary()); - } - - await userService.ResetPasswordAsync(command, cancellationToken); - return Results.Ok("Password has been reset."); - }) - .WithName(nameof(ResetPasswordEndpoint)) - .WithSummary("Reset password") - .WithDescription("Resets the password using the token and new password provided.") - .AllowAnonymous(); - } - -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/SelfRegisterUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/SelfRegisterUserEndpoint.cs deleted file mode 100644 index 8af1fc52f0..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/SelfRegisterUserEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.RegisterUser; -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class SelfRegisterUserEndpoint -{ - internal static RouteHandlerBuilder MapSelfRegisterUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/self-register", (RegisterUserCommand request, - [FromHeader(Name = TenantConstants.Identifier)] string tenant, - IUserService service, - HttpContext context, - CancellationToken cancellationToken) => - { - var origin = $"{context.Request.Scheme}://{context.Request.Host.Value}{context.Request.PathBase.Value}"; - return service.RegisterAsync(request, origin, cancellationToken); - }) - .WithName(nameof(SelfRegisterUserEndpoint)) - .WithSummary("self register user") - .RequirePermission("Permissions.Users.Create") - .WithDescription("self register user") - .AllowAnonymous(); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ToggleUserStatusEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/ToggleUserStatusEndpoint.cs deleted file mode 100644 index 7e705e3294..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/ToggleUserStatusEndpoint.cs +++ /dev/null @@ -1,35 +0,0 @@ -using FluentValidation; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.ToggleUserStatus; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; - -public static class ToggleUserStatusEndpoint -{ - internal static RouteHandlerBuilder ToggleUserStatusEndpointEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/{id:guid}/toggle-status", async ( - string id, - ToggleUserStatusCommand command, - [FromServices] IUserService userService, - CancellationToken cancellationToken) => - { - if (id != command.UserId) - { - return Results.BadRequest(); - } - - await userService.ToggleStatusAsync(command, cancellationToken); - return Results.Ok(); - }) - .WithName(nameof(ToggleUserStatusEndpoint)) - .WithSummary("Toggle a user's active status") - .WithDescription("Toggle a user's active status") - .AllowAnonymous(); - } - -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Endpoints/UpdateUserEndpoint.cs b/src/api/framework/Infrastructure/Identity/Users/Endpoints/UpdateUserEndpoint.cs deleted file mode 100644 index 6d137e8d99..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Endpoints/UpdateUserEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Security.Claims; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Features.UpdateUser; -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.Shared.Authorization; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Identity.Users.Endpoints; -public static class UpdateUserEndpoint -{ - internal static RouteHandlerBuilder MapUpdateUserEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPut("/profile", (UpdateUserCommand request, ISender mediator, ClaimsPrincipal user, IUserService service) => - { - if (user.GetUserId() is not { } userId || string.IsNullOrEmpty(userId)) - { - throw new UnauthorizedException(); - } - return service.UpdateAsync(request, userId); - }) - .WithName(nameof(UpdateUserEndpoint)) - .WithSummary("update user profile") - .RequirePermission("Permissions.Users.Update") - .WithDescription("update user profile"); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/FshUser.cs b/src/api/framework/Infrastructure/Identity/Users/FshUser.cs deleted file mode 100644 index 4d68e207f3..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/FshUser.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace FSH.Framework.Infrastructure.Identity.Users; -public class FshUser : IdentityUser -{ - public string? FirstName { get; set; } - public string? LastName { get; set; } - public Uri? ImageUrl { get; set; } - public bool IsActive { get; set; } - public string? RefreshToken { get; set; } - public DateTime RefreshTokenExpiryTime { get; set; } - - public string? ObjectId { get; set; } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Services/CurrentUserService.cs b/src/api/framework/Infrastructure/Identity/Users/Services/CurrentUserService.cs deleted file mode 100644 index 2fcfea6fb5..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Services/CurrentUserService.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Security.Claims; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Starter.Shared.Authorization; - -namespace FSH.Framework.Infrastructure.Identity.Users.Services; -public class CurrentUser : ICurrentUser, ICurrentUserInitializer -{ - private ClaimsPrincipal? _user; - - public string? Name => _user?.Identity?.Name; - - private Guid _userId = Guid.Empty; - - public Guid GetUserId() - { - return IsAuthenticated() - ? Guid.Parse(_user?.GetUserId() ?? Guid.Empty.ToString()) - : _userId; - } - - public string? GetUserEmail() => - IsAuthenticated() - ? _user!.GetEmail() - : string.Empty; - - public bool IsAuthenticated() => - _user?.Identity?.IsAuthenticated is true; - - public bool IsInRole(string role) => - _user?.IsInRole(role) is true; - - public IEnumerable? GetUserClaims() => - _user?.Claims; - - public string? GetTenant() => - IsAuthenticated() ? _user?.GetTenant() : string.Empty; - - public void SetCurrentUser(ClaimsPrincipal user) - { - if (_user != null) - { - throw new FshException("Method reserved for in-scope initialization"); - } - - _user = user; - } - - public void SetCurrentUserId(string userId) - { - if (_userId != Guid.Empty) - { - throw new FshException("Method reserved for in-scope initialization"); - } - - if (!string.IsNullOrEmpty(userId)) - { - _userId = Guid.Parse(userId); - } - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Password.cs b/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Password.cs deleted file mode 100644 index 97b7af7979..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Password.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Collections.ObjectModel; -using System.Text; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Users.Features.ChangePassword; -using FSH.Framework.Core.Identity.Users.Features.ForgotPassword; -using FSH.Framework.Core.Identity.Users.Features.ResetPassword; -using FSH.Framework.Core.Mail; -using Microsoft.AspNetCore.WebUtilities; - -namespace FSH.Framework.Infrastructure.Identity.Users.Services; -internal sealed partial class UserService -{ - public async Task ForgotPasswordAsync(ForgotPasswordCommand request, string origin, CancellationToken cancellationToken) - { - EnsureValidTenant(); - - var user = await userManager.FindByEmailAsync(request.Email); - if (user == null) - { - throw new NotFoundException("user not found"); - } - - if (string.IsNullOrWhiteSpace(user.Email)) - { - throw new InvalidOperationException("user email cannot be null or empty"); - } - - var token = await userManager.GeneratePasswordResetTokenAsync(user); - token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token)); - - var resetPasswordUri = $"{origin}/reset-password?token={token}&email={request.Email}"; - var mailRequest = new MailRequest( - new Collection { user.Email }, - "Reset Password", - $"Please reset your password using the following link: {resetPasswordUri}"); - - jobService.Enqueue(() => mailService.SendAsync(mailRequest, CancellationToken.None)); - } - - public async Task ResetPasswordAsync(ResetPasswordCommand request, CancellationToken cancellationToken) - { - EnsureValidTenant(); - - var user = await userManager.FindByEmailAsync(request.Email); - if (user == null) - { - throw new NotFoundException("user not found"); - } - - request.Token = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(request.Token)); - var result = await userManager.ResetPasswordAsync(user, request.Token, request.Password); - - if (!result.Succeeded) - { - var errors = result.Errors.Select(e => e.Description).ToList(); - throw new FshException("error resetting password", errors); - } - } - - public async Task ChangePasswordAsync(ChangePasswordCommand request, string userId) - { - var user = await userManager.FindByIdAsync(userId); - - _ = user ?? throw new NotFoundException("user not found"); - - var result = await userManager.ChangePasswordAsync(user, request.Password, request.NewPassword); - - if (!result.Succeeded) - { - var errors = result.Errors.Select(e => e.Description).ToList(); - throw new FshException("failed to change password", errors); - } - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Permissions.cs b/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Permissions.cs deleted file mode 100644 index 52f5698f78..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.Permissions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using FSH.Framework.Core.Caching; -using FSH.Framework.Core.Exceptions; -using FSH.Starter.Shared.Authorization; -using Microsoft.EntityFrameworkCore; - -namespace FSH.Framework.Infrastructure.Identity.Users.Services; -internal sealed partial class UserService -{ - public async Task?> GetPermissionsAsync(string userId, CancellationToken cancellationToken) - { - var permissions = await cache.GetOrSetAsync( - GetPermissionCacheKey(userId), - async () => - { - var user = await userManager.FindByIdAsync(userId); - - _ = user ?? throw new UnauthorizedException(); - - var userRoles = await userManager.GetRolesAsync(user); - var permissions = new List(); - foreach (var role in await roleManager.Roles - .Where(r => userRoles.Contains(r.Name!)) - .ToListAsync(cancellationToken)) - { - permissions.AddRange(await db.RoleClaims - .Where(rc => rc.RoleId == role.Id && rc.ClaimType == FshClaims.Permission) - .Select(rc => rc.ClaimValue!) - .ToListAsync(cancellationToken)); - } - return permissions.Distinct().ToList(); - }, - cancellationToken: cancellationToken); - - return permissions; - } - - public static string GetPermissionCacheKey(string userId) - { - return $"perm:{userId}"; - } - - public async Task HasPermissionAsync(string userId, string permission, CancellationToken cancellationToken = default) - { - var permissions = await GetPermissionsAsync(userId, cancellationToken); - - return permissions?.Contains(permission) ?? false; - } - - public Task InvalidatePermissionCacheAsync(string userId, CancellationToken cancellationToken) - { - return cache.RemoveAsync(GetPermissionCacheKey(userId), cancellationToken); - } -} diff --git a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.cs b/src/api/framework/Infrastructure/Identity/Users/Services/UserService.cs deleted file mode 100644 index a714a154d2..0000000000 --- a/src/api/framework/Infrastructure/Identity/Users/Services/UserService.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System.Collections.ObjectModel; -using System.Security.Claims; -using System.Text; -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Caching; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Core.Identity.Users.Dtos; -using FSH.Framework.Core.Identity.Users.Features.AssignUserRole; -using FSH.Framework.Core.Identity.Users.Features.RegisterUser; -using FSH.Framework.Core.Identity.Users.Features.ToggleUserStatus; -using FSH.Framework.Core.Identity.Users.Features.UpdateUser; -using FSH.Framework.Core.Jobs; -using FSH.Framework.Core.Mail; -using FSH.Framework.Core.Storage; -using FSH.Framework.Core.Storage.File; -using FSH.Framework.Infrastructure.Constants; -using FSH.Framework.Infrastructure.Identity.Persistence; -using FSH.Framework.Infrastructure.Identity.Roles; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.EntityFrameworkCore; - -namespace FSH.Framework.Infrastructure.Identity.Users.Services; - -internal sealed partial class UserService( - UserManager userManager, - SignInManager signInManager, - RoleManager roleManager, - IdentityDbContext db, - ICacheService cache, - IJobService jobService, - IMailService mailService, - IMultiTenantContextAccessor multiTenantContextAccessor, - IStorageService storageService - ) : IUserService -{ - private void EnsureValidTenant() - { - if (string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id)) - { - throw new UnauthorizedException("invalid tenant"); - } - } - - public async Task ConfirmEmailAsync(string userId, string code, string tenant, CancellationToken cancellationToken) - { - EnsureValidTenant(); - - var user = await userManager.Users - .Where(u => u.Id == userId && !u.EmailConfirmed) - .FirstOrDefaultAsync(cancellationToken); - - _ = user ?? throw new FshException("An error occurred while confirming E-Mail."); - - code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); - var result = await userManager.ConfirmEmailAsync(user, code); - - return result.Succeeded - ? string.Format("Account Confirmed for E-Mail {0}. You can now use the /api/tokens endpoint to generate JWT.", user.Email) - : throw new FshException(string.Format("An error occurred while confirming {0}", user.Email)); - } - - public Task ConfirmPhoneNumberAsync(string userId, string code) - { - throw new NotImplementedException(); - } - - public async Task ExistsWithEmailAsync(string email, string? exceptId = null) - { - EnsureValidTenant(); - return await userManager.FindByEmailAsync(email.Normalize()) is FshUser user && user.Id != exceptId; - } - - public async Task ExistsWithNameAsync(string name) - { - EnsureValidTenant(); - return await userManager.FindByNameAsync(name) is not null; - } - - public async Task ExistsWithPhoneNumberAsync(string phoneNumber, string? exceptId = null) - { - EnsureValidTenant(); - return await userManager.Users.FirstOrDefaultAsync(x => x.PhoneNumber == phoneNumber) is FshUser user && user.Id != exceptId; - } - - public async Task GetAsync(string userId, CancellationToken cancellationToken) - { - var user = await userManager.Users - .AsNoTracking() - .Where(u => u.Id == userId) - .FirstOrDefaultAsync(cancellationToken); - - _ = user ?? throw new NotFoundException("user not found"); - - return user.Adapt(); - } - - public Task GetCountAsync(CancellationToken cancellationToken) => - userManager.Users.AsNoTracking().CountAsync(cancellationToken); - - public async Task> GetListAsync(CancellationToken cancellationToken) - { - var users = await userManager.Users.AsNoTracking().ToListAsync(cancellationToken); - return users.Adapt>(); - } - - public Task GetOrCreateFromPrincipalAsync(ClaimsPrincipal principal) - { - throw new NotImplementedException(); - } - - public async Task RegisterAsync(RegisterUserCommand request, string origin, CancellationToken cancellationToken) - { - // create user entity - var user = new FshUser - { - Email = request.Email, - FirstName = request.FirstName, - LastName = request.LastName, - UserName = request.UserName, - PhoneNumber = request.PhoneNumber, - IsActive = true, - EmailConfirmed = false, - PhoneNumberConfirmed = false, - }; - - // register user - var result = await userManager.CreateAsync(user, request.Password); - if (!result.Succeeded) - { - var errors = result.Errors.Select(error => error.Description).ToList(); - throw new FshException("error while registering a new user", errors); - } - - // add basic role - await userManager.AddToRoleAsync(user, FshRoles.Basic); - - // send confirmation mail - if (!string.IsNullOrEmpty(user.Email)) - { - string emailVerificationUri = await GetEmailVerificationUriAsync(user, origin); - var mailRequest = new MailRequest( - new Collection { user.Email }, - "Confirm Registration", - emailVerificationUri); - jobService.Enqueue("email", () => mailService.SendAsync(mailRequest, CancellationToken.None)); - } - - return new RegisterUserResponse(user.Id); - } - - public async Task ToggleStatusAsync(ToggleUserStatusCommand request, CancellationToken cancellationToken) - { - var user = await userManager.Users.Where(u => u.Id == request.UserId).FirstOrDefaultAsync(cancellationToken); - - _ = user ?? throw new NotFoundException("User Not Found."); - - bool isAdmin = await userManager.IsInRoleAsync(user, FshRoles.Admin); - if (isAdmin) - { - throw new FshException("Administrators Profile's Status cannot be toggled"); - } - - user.IsActive = request.ActivateUser; - - await userManager.UpdateAsync(user); - } - - public async Task UpdateAsync(UpdateUserCommand request, string userId) - { - var user = await userManager.FindByIdAsync(userId); - - _ = user ?? throw new NotFoundException("user not found"); - - Uri imageUri = user.ImageUrl ?? null!; - if (request.Image != null || request.DeleteCurrentImage) - { - user.ImageUrl = await storageService.UploadAsync(request.Image, FileType.Image); - if (request.DeleteCurrentImage && imageUri != null) - { - storageService.Remove(imageUri); - } - } - - user.FirstName = request.FirstName; - user.LastName = request.LastName; - user.PhoneNumber = request.PhoneNumber; - string? phoneNumber = await userManager.GetPhoneNumberAsync(user); - if (request.PhoneNumber != phoneNumber) - { - await userManager.SetPhoneNumberAsync(user, request.PhoneNumber); - } - - var result = await userManager.UpdateAsync(user); - await signInManager.RefreshSignInAsync(user); - - if (!result.Succeeded) - { - throw new FshException("Update profile failed"); - } - } - - public async Task DeleteAsync(string userId) - { - FshUser? user = await userManager.FindByIdAsync(userId); - - _ = user ?? throw new NotFoundException("User Not Found."); - - user.IsActive = false; - IdentityResult? result = await userManager.UpdateAsync(user); - - if (!result.Succeeded) - { - List errors = result.Errors.Select(error => error.Description).ToList(); - throw new FshException("Delete profile failed", errors); - } - } - - private async Task GetEmailVerificationUriAsync(FshUser user, string origin) - { - EnsureValidTenant(); - - string code = await userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - const string route = "api/users/confirm-email/"; - var endpointUri = new Uri(string.Concat($"{origin}/", route)); - string verificationUri = QueryHelpers.AddQueryString(endpointUri.ToString(), QueryStringKeys.UserId, user.Id); - verificationUri = QueryHelpers.AddQueryString(verificationUri, QueryStringKeys.Code, code); - verificationUri = QueryHelpers.AddQueryString(verificationUri, - TenantConstants.Identifier, - multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id!); - return verificationUri; - } - - public async Task AssignRolesAsync(string userId, AssignUserRoleCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - - var user = await userManager.Users.Where(u => u.Id == userId).FirstOrDefaultAsync(cancellationToken); - - _ = user ?? throw new NotFoundException("user not found"); - - // Check if the user is an admin for which the admin role is getting disabled - if (await userManager.IsInRoleAsync(user, FshRoles.Admin) - && request.UserRoles.Exists(a => !a.Enabled && a.RoleName == FshRoles.Admin)) - { - // Get count of users in Admin Role - int adminCount = (await userManager.GetUsersInRoleAsync(FshRoles.Admin)).Count; - - // Check if user is not Root Tenant Admin - // Edge Case : there are chances for other tenants to have users with the same email as that of Root Tenant Admin. Probably can add a check while User Registration - if (user.Email == TenantConstants.Root.EmailAddress) - { - if (multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id == TenantConstants.Root.Id) - { - throw new FshException("action not permitted"); - } - } - else if (adminCount <= 2) - { - throw new FshException("tenant should have at least 2 admins."); - } - } - - foreach (var userRole in request.UserRoles) - { - // Check if Role Exists - if (await roleManager.FindByNameAsync(userRole.RoleName!) is not null) - { - if (userRole.Enabled) - { - if (!await userManager.IsInRoleAsync(user, userRole.RoleName!)) - { - await userManager.AddToRoleAsync(user, userRole.RoleName!); - } - } - else - { - await userManager.RemoveFromRoleAsync(user, userRole.RoleName!); - } - } - } - - return "User Roles Updated Successfully."; - - } - - public async Task> GetUserRolesAsync(string userId, CancellationToken cancellationToken) - { - var userRoles = new List(); - - var user = await userManager.FindByIdAsync(userId); - if (user is null) throw new NotFoundException("user not found"); - var roles = await roleManager.Roles.AsNoTracking().ToListAsync(cancellationToken); - if (roles is null) throw new NotFoundException("roles not found"); - foreach (var role in roles) - { - userRoles.Add(new UserRoleDetail - { - RoleId = role.Id, - RoleName = role.Name, - Description = role.Description, - Enabled = await userManager.IsInRoleAsync(user, role.Name!) - }); - } - - return userRoles; - } -} diff --git a/src/api/framework/Infrastructure/Infrastructure.csproj b/src/api/framework/Infrastructure/Infrastructure.csproj deleted file mode 100644 index 389248b9c6..0000000000 --- a/src/api/framework/Infrastructure/Infrastructure.csproj +++ /dev/null @@ -1,82 +0,0 @@ - - - FSH.Framework.Infrastructure - FSH.Framework.Infrastructure - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/api/framework/Infrastructure/Jobs/Extensions.cs b/src/api/framework/Infrastructure/Jobs/Extensions.cs deleted file mode 100644 index 618d07d30d..0000000000 --- a/src/api/framework/Infrastructure/Jobs/Extensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Jobs; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence; -using Hangfire; -using Hangfire.PostgreSql; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Framework.Infrastructure.Jobs; - -internal static class Extensions -{ - internal static IServiceCollection ConfigureJobs(this IServiceCollection services, IConfiguration configuration) - { - var dbOptions = configuration.GetSection(nameof(DatabaseOptions)).Get() ?? - throw new FshException("database options cannot be null"); - - services.AddHangfireServer(o => - { - o.HeartbeatInterval = TimeSpan.FromSeconds(30); - o.Queues = new string[] { "default", "email" }; - o.WorkerCount = 5; - o.SchedulePollingInterval = TimeSpan.FromSeconds(30); - }); - - services.AddHangfire((provider, config) => - { - switch (dbOptions.Provider.ToUpperInvariant()) - { - case DbProviders.PostgreSQL: - config.UsePostgreSqlStorage(o => - { - o.UseNpgsqlConnection(dbOptions.ConnectionString); - }); - break; - - case DbProviders.MSSQL: - config.UseSqlServerStorage(dbOptions.ConnectionString); - break; - - default: - throw new FshException($"hangfire storage provider {dbOptions.Provider} is not supported"); - } - - config.UseFilter(new FshJobFilter(provider)); - config.UseFilter(new LogJobFilter()); - }); - - services.AddTransient(); - return services; - } - - internal static IApplicationBuilder UseJobDashboard(this IApplicationBuilder app, IConfiguration config) - { - var hangfireOptions = config.GetSection(nameof(HangfireOptions)).Get() ?? new HangfireOptions(); - var dashboardOptions = new DashboardOptions(); - dashboardOptions.AppPath = "https://fullstackhero.net/"; - dashboardOptions.Authorization = new[] - { - new HangfireCustomBasicAuthenticationFilter - { - User = hangfireOptions.UserName!, - Pass = hangfireOptions.Password! - } - }; - - return app.UseHangfireDashboard(hangfireOptions.Route, dashboardOptions); - } -} diff --git a/src/api/framework/Infrastructure/Jobs/FshJobActivator.cs b/src/api/framework/Infrastructure/Jobs/FshJobActivator.cs deleted file mode 100644 index dc0eb2fd83..0000000000 --- a/src/api/framework/Infrastructure/Jobs/FshJobActivator.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Finbuckle.MultiTenant; -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Constants; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.Shared.Authorization; -using Hangfire; -using Hangfire.Server; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Framework.Infrastructure.Jobs; - -public class FshJobActivator : JobActivator -{ - private readonly IServiceScopeFactory _scopeFactory; - - public FshJobActivator(IServiceScopeFactory scopeFactory) => - _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); - - public override JobActivatorScope BeginScope(PerformContext context) => - new Scope(context, _scopeFactory.CreateScope()); - - private sealed class Scope : JobActivatorScope, IServiceProvider - { - private readonly PerformContext _context; - private readonly IServiceScope _scope; - - public Scope(PerformContext context, IServiceScope scope) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - _scope = scope ?? throw new ArgumentNullException(nameof(scope)); - - ReceiveParameters(); - } - - private void ReceiveParameters() - { - var tenantInfo = _context.GetJobParameter(TenantConstants.Identifier); - if (tenantInfo is not null) - { - _scope.ServiceProvider.GetRequiredService() - .MultiTenantContext = new MultiTenantContext - { - TenantInfo = tenantInfo - }; - } - - string userId = _context.GetJobParameter(QueryStringKeys.UserId); - if (!string.IsNullOrEmpty(userId)) - { - _scope.ServiceProvider.GetRequiredService() - .SetCurrentUserId(userId); - } - } - - public override object Resolve(Type type) => - ActivatorUtilities.GetServiceOrCreateInstance(this, type); - - object? IServiceProvider.GetService(Type serviceType) => - serviceType == typeof(PerformContext) - ? _context - : _scope.ServiceProvider.GetService(serviceType); - } -} diff --git a/src/api/framework/Infrastructure/Jobs/FshJobFilter.cs b/src/api/framework/Infrastructure/Jobs/FshJobFilter.cs deleted file mode 100644 index 54b83641b2..0000000000 --- a/src/api/framework/Infrastructure/Jobs/FshJobFilter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Infrastructure.Constants; -using FSH.Starter.Shared.Authorization; -using Hangfire.Client; -using Hangfire.Logging; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Framework.Infrastructure.Jobs; - -public class FshJobFilter : IClientFilter -{ - private static readonly ILog Logger = LogProvider.GetCurrentClassLogger(); - - private readonly IServiceProvider _services; - - public FshJobFilter(IServiceProvider services) => _services = services; - - public void OnCreating(CreatingContext context) - { - ArgumentNullException.ThrowIfNull(context); - - Logger.InfoFormat("Set TenantId and UserId parameters to job {0}.{1}...", context.Job.Method.ReflectedType?.FullName, context.Job.Method.Name); - - using var scope = _services.CreateScope(); - - var httpContext = scope.ServiceProvider.GetRequiredService()?.HttpContext; - _ = httpContext ?? throw new InvalidOperationException("Can't create a TenantJob without HttpContext."); - - var tenantInfo = scope.ServiceProvider.GetRequiredService().MultiTenantContext.TenantInfo; - context.SetJobParameter(TenantConstants.Identifier, tenantInfo); - - string? userId = httpContext.User.GetUserId(); - context.SetJobParameter(QueryStringKeys.UserId, userId); - } - - public void OnCreated(CreatedContext context) => - Logger.InfoFormat( - "Job created with parameters {0}", - context.Parameters.Select(x => x.Key + "=" + x.Value).Aggregate((s1, s2) => s1 + ";" + s2)); -} diff --git a/src/api/framework/Infrastructure/Jobs/HangfireCustomBasicAuthenticationFilter.cs b/src/api/framework/Infrastructure/Jobs/HangfireCustomBasicAuthenticationFilter.cs deleted file mode 100644 index 2cc8980d4b..0000000000 --- a/src/api/framework/Infrastructure/Jobs/HangfireCustomBasicAuthenticationFilter.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System.Net.Http.Headers; -using Hangfire.Dashboard; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Primitives; - -namespace FSH.Framework.Infrastructure.Jobs; - -public class HangfireCustomBasicAuthenticationFilter : IDashboardAuthorizationFilter -{ - private const string _AuthenticationScheme = "Basic"; - private readonly ILogger _logger; - public string User { get; set; } = default!; - public string Pass { get; set; } = default!; - - public HangfireCustomBasicAuthenticationFilter() - : this(new NullLogger()) - { - } - - public HangfireCustomBasicAuthenticationFilter(ILogger logger) => _logger = logger; - - public bool Authorize(DashboardContext context) - { - var httpContext = context.GetHttpContext(); - var header = httpContext.Request.Headers["Authorization"]!; - - if (MissingAuthorizationHeader(header)) - { - _logger.LogInformation("Request is missing Authorization Header"); - SetChallengeResponse(httpContext); - return false; - } - - var authValues = AuthenticationHeaderValue.Parse(header!); - - if (NotBasicAuthentication(authValues)) - { - _logger.LogInformation("Request is NOT BASIC authentication"); - SetChallengeResponse(httpContext); - return false; - } - - var tokens = ExtractAuthenticationTokens(authValues); - - if (tokens.AreInvalid()) - { - _logger.LogInformation("Authentication tokens are invalid (empty, null, whitespace)"); - SetChallengeResponse(httpContext); - return false; - } - - if (tokens.CredentialsMatch(User, Pass)) - { - _logger.LogInformation("Awesome, authentication tokens match configuration!"); - return true; - } - - _logger.LogInformation("auth tokens [{UserName}] [{Password}] do not match configuration", tokens.Username, tokens.Password); - - SetChallengeResponse(httpContext); - return false; - } - - private static bool MissingAuthorizationHeader(StringValues header) - { - return string.IsNullOrWhiteSpace(header); - } - - private static BasicAuthenticationTokens ExtractAuthenticationTokens(AuthenticationHeaderValue authValues) - { - string? parameter = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authValues.Parameter!)); - string[]? parts = parameter.Split(':'); - return new BasicAuthenticationTokens(parts); - } - - private static bool NotBasicAuthentication(AuthenticationHeaderValue authValues) - { - return !_AuthenticationScheme.Equals(authValues.Scheme, StringComparison.OrdinalIgnoreCase); - } - - private static void SetChallengeResponse(HttpContext httpContext) - { - httpContext.Response.StatusCode = 401; - httpContext.Response.Headers.Append("WWW-Authenticate", "Basic realm=\"Hangfire Dashboard\""); - } -} - -public class BasicAuthenticationTokens -{ - private readonly string[] _tokens; - - public string Username => _tokens[0]; - public string Password => _tokens[1]; - - public BasicAuthenticationTokens(string[] tokens) - { - _tokens = tokens; - } - - public bool AreInvalid() - { - return ContainsTwoTokens() && ValidTokenValue(Username) && ValidTokenValue(Password); - } - - public bool CredentialsMatch(string user, string pass) - { - return Username.Equals(user, StringComparison.Ordinal) && Password.Equals(pass, StringComparison.Ordinal); - } - - private static bool ValidTokenValue(string token) - { - return string.IsNullOrWhiteSpace(token); - } - - private bool ContainsTwoTokens() - { - return _tokens.Length == 2; - } -} diff --git a/src/api/framework/Infrastructure/Jobs/HangfireOptions.cs b/src/api/framework/Infrastructure/Jobs/HangfireOptions.cs deleted file mode 100644 index 45f5ac0c63..0000000000 --- a/src/api/framework/Infrastructure/Jobs/HangfireOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Infrastructure.Jobs; -public class HangfireOptions -{ - public string UserName { get; set; } = "admin"; - public string Password { get; set; } = "Secure1234!Me"; - public string Route { get; set; } = "/jobs"; -} diff --git a/src/api/framework/Infrastructure/Jobs/HangfireService.cs b/src/api/framework/Infrastructure/Jobs/HangfireService.cs deleted file mode 100644 index 1c4785cd10..0000000000 --- a/src/api/framework/Infrastructure/Jobs/HangfireService.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Linq.Expressions; -using FSH.Framework.Core.Jobs; -using Hangfire; - -namespace FSH.Framework.Infrastructure.Jobs; - -public class HangfireService : IJobService -{ - public bool Delete(string jobId) => - BackgroundJob.Delete(jobId); - - public bool Delete(string jobId, string fromState) => - BackgroundJob.Delete(jobId, fromState); - - public string Enqueue(Expression> methodCall) => - BackgroundJob.Enqueue(methodCall); - - public string Enqueue(string queue, Expression> methodCall) => - BackgroundJob.Enqueue(queue, methodCall); - - public string Enqueue(Expression> methodCall) => - BackgroundJob.Enqueue(methodCall); - - public string Enqueue(Expression methodCall) => - BackgroundJob.Enqueue(methodCall); - - public string Enqueue(Expression> methodCall) => - BackgroundJob.Enqueue(methodCall); - - public bool Requeue(string jobId) => - BackgroundJob.Requeue(jobId); - - public bool Requeue(string jobId, string fromState) => - BackgroundJob.Requeue(jobId, fromState); - - public string Schedule(Expression methodCall, TimeSpan delay) => - BackgroundJob.Schedule(methodCall, delay); - - public string Schedule(Expression> methodCall, TimeSpan delay) => - BackgroundJob.Schedule(methodCall, delay); - - public string Schedule(Expression methodCall, DateTimeOffset enqueueAt) => - BackgroundJob.Schedule(methodCall, enqueueAt); - - public string Schedule(Expression> methodCall, DateTimeOffset enqueueAt) => - BackgroundJob.Schedule(methodCall, enqueueAt); - - public string Schedule(Expression> methodCall, TimeSpan delay) => - BackgroundJob.Schedule(methodCall, delay); - - public string Schedule(Expression> methodCall, TimeSpan delay) => - BackgroundJob.Schedule(methodCall, delay); - - public string Schedule(Expression> methodCall, DateTimeOffset enqueueAt) => - BackgroundJob.Schedule(methodCall, enqueueAt); - - public string Schedule(Expression> methodCall, DateTimeOffset enqueueAt) => - BackgroundJob.Schedule(methodCall, enqueueAt); -} diff --git a/src/api/framework/Infrastructure/Jobs/LogJobFilter.cs b/src/api/framework/Infrastructure/Jobs/LogJobFilter.cs deleted file mode 100644 index 24f1c734ea..0000000000 --- a/src/api/framework/Infrastructure/Jobs/LogJobFilter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Hangfire.Client; -using Hangfire.Logging; -using Hangfire.Server; -using Hangfire.States; -using Hangfire.Storage; - -namespace FSH.Framework.Infrastructure.Jobs; - -public class LogJobFilter : IClientFilter, IServerFilter, IElectStateFilter, IApplyStateFilter -{ - private static readonly ILog Logger = LogProvider.GetCurrentClassLogger(); - - public void OnCreating(CreatingContext context) => - Logger.DebugFormat("Creating a job based on method {0}...", context.Job.Method.Name); - - public void OnCreated(CreatedContext context) => - Logger.DebugFormat( - "Job that is based on method {0} has been created with id {1}", - context.Job.Method.Name, - context.BackgroundJob?.Id); - - public void OnPerforming(PerformingContext context) => - Logger.DebugFormat("Starting to perform job {0}", context.BackgroundJob.Id); - - public void OnPerformed(PerformedContext context) => - Logger.DebugFormat("Job {0} has been performed", context.BackgroundJob.Id); - - public void OnStateElection(ElectStateContext context) - { - if (context.CandidateState is FailedState failedState) - { - Logger.WarnFormat( - "Job '{0}' has been failed due to an exception {1}", - context.BackgroundJob.Id, - failedState.Exception); - } - } - - public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) => - Logger.DebugFormat( - "Job {0} state was changed from {1} to {2}", - context.BackgroundJob.Id, - context.OldStateName, - context.NewState.Name); - - public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction) => - Logger.DebugFormat( - "Job {0} state {1} was unapplied.", - context.BackgroundJob.Id, - context.OldStateName); -} diff --git a/src/api/framework/Infrastructure/Logging/Serilog/Extensions.cs b/src/api/framework/Infrastructure/Logging/Serilog/Extensions.cs deleted file mode 100644 index 25a8dba177..0000000000 --- a/src/api/framework/Infrastructure/Logging/Serilog/Extensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Serilog; -using Serilog.Events; -using Serilog.Filters; - -namespace FSH.Framework.Infrastructure.Logging.Serilog; - -public static class Extensions -{ - public static WebApplicationBuilder ConfigureSerilog(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - builder.Host.UseSerilog((context, logger) => - { - logger.WriteTo.OpenTelemetry(options => - { - try - { - options.Endpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]; - var headers = builder.Configuration["OTEL_EXPORTER_OTLP_HEADERS"]?.Split(',') ?? []; - foreach (var header in headers) - { - var (key, value) = header.Split('=') switch - { - [string k, string v] => (k, v), - var v => throw new Exception($"Invalid header format {v}") - }; - - options.Headers.Add(key, value); - } - options.ResourceAttributes.Add("service.name", "apiservice"); - //To remove the duplicate issue, we can use the below code to get the key and value from the configuration - var (otelResourceAttribute, otelResourceAttributeValue) = builder.Configuration["OTEL_RESOURCE_ATTRIBUTES"]?.Split('=') switch - { - [string k, string v] => (k, v), - _ => throw new Exception($"Invalid header format {builder.Configuration["OTEL_RESOURCE_ATTRIBUTES"]}") - }; - options.ResourceAttributes.Add(otelResourceAttribute, otelResourceAttributeValue); - } - catch - { - //ignore - } - }); - logger.ReadFrom.Configuration(context.Configuration); - logger.Enrich.FromLogContext(); - logger.Enrich.WithCorrelationId(); - logger - .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) - .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) - .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Error) - .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) - .MinimumLevel.Override("Finbuckle.MultiTenant", LogEventLevel.Warning) - .Filter.ByExcluding(Matching.FromSource("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware")); - }); - return builder; - } -} diff --git a/src/api/framework/Infrastructure/Logging/Serilog/StaticLogger.cs b/src/api/framework/Infrastructure/Logging/Serilog/StaticLogger.cs deleted file mode 100644 index 1809893b53..0000000000 --- a/src/api/framework/Infrastructure/Logging/Serilog/StaticLogger.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Serilog; -using Serilog.Core; - -namespace FSH.Framework.Infrastructure.Logging.Serilog; - -public static class StaticLogger -{ - public static void EnsureInitialized() - { - if (Log.Logger is not Logger) - { - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Console() - .WriteTo.OpenTelemetry() - .CreateLogger(); - } - } -} diff --git a/src/api/framework/Infrastructure/Mail/Extensions.cs b/src/api/framework/Infrastructure/Mail/Extensions.cs deleted file mode 100644 index 4c772f7731..0000000000 --- a/src/api/framework/Infrastructure/Mail/Extensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FSH.Framework.Core.Mail; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Framework.Infrastructure.Mail; -internal static class Extensions -{ - internal static IServiceCollection ConfigureMailing(this IServiceCollection services) - { - services.AddTransient(); - services.AddOptions().BindConfiguration(nameof(MailOptions)); - return services; - } -} diff --git a/src/api/framework/Infrastructure/Mail/SmtpMailService.cs b/src/api/framework/Infrastructure/Mail/SmtpMailService.cs deleted file mode 100644 index aee5969491..0000000000 --- a/src/api/framework/Infrastructure/Mail/SmtpMailService.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace FSH.Framework.Infrastructure.Mail; -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using FSH.Framework.Core.Mail; -using MailKit.Net.Smtp; -using MailKit.Security; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using MimeKit; - -public class SmtpMailService(IOptions settings, ILogger logger) : IMailService -{ - private readonly MailOptions _settings = settings.Value; - private readonly ILogger _logger = logger; - - public async Task SendAsync(MailRequest request, CancellationToken ct) - { - using var email = new MimeMessage(); - - // From - email.From.Add(new MailboxAddress(_settings.DisplayName, request.From ?? _settings.From)); - - // To - foreach (string address in request.To) - email.To.Add(MailboxAddress.Parse(address)); - - // Reply To - if (!string.IsNullOrEmpty(request.ReplyTo)) - email.ReplyTo.Add(new MailboxAddress(request.ReplyToName, request.ReplyTo)); - - // Bcc - if (request.Bcc != null) - { - foreach (string address in request.Bcc.Where(bccValue => !string.IsNullOrWhiteSpace(bccValue))) - email.Bcc.Add(MailboxAddress.Parse(address.Trim())); - } - - // Cc - if (request.Cc != null) - { - foreach (string? address in request.Cc.Where(ccValue => !string.IsNullOrWhiteSpace(ccValue))) - email.Cc.Add(MailboxAddress.Parse(address.Trim())); - } - - // Headers - if (request.Headers != null) - { - foreach (var header in request.Headers) - email.Headers.Add(header.Key, header.Value); - } - - // Content - var builder = new BodyBuilder(); - email.Sender = new MailboxAddress(request.DisplayName ?? _settings.DisplayName, request.From ?? _settings.From); - email.Subject = request.Subject; - builder.HtmlBody = request.Body; - - // Create the file attachments for this e-mail message - if (request.AttachmentData != null) - { - foreach (var attachmentInfo in request.AttachmentData) - { - using (var stream = new MemoryStream()) - { - await stream.WriteAsync(attachmentInfo.Value, ct); - stream.Position = 0; - await builder.Attachments.AddAsync(attachmentInfo.Key, stream, ct); - } - } - } - - email.Body = builder.ToMessageBody(); - - using var client = new SmtpClient(); - try - { - await client.ConnectAsync(_settings.Host, _settings.Port, SecureSocketOptions.StartTls, ct); - await client.AuthenticateAsync(_settings.UserName, _settings.Password, ct); - await client.SendAsync(email, ct); - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while sending email: {Message}", ex.Message); - } - finally - { - await client.DisconnectAsync(true, ct); - } - } -} diff --git a/src/api/framework/Infrastructure/OpenApi/ConfigureSwaggerOptions.cs b/src/api/framework/Infrastructure/OpenApi/ConfigureSwaggerOptions.cs deleted file mode 100644 index 71eed3eb74..0000000000 --- a/src/api/framework/Infrastructure/OpenApi/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text; -using Asp.Versioning.ApiExplorer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace FSH.Framework.Infrastructure.OpenApi; -public class ConfigureSwaggerOptions : IConfigureOptions -{ - private readonly IApiVersionDescriptionProvider provider; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var text = new StringBuilder(".NET 8 Starter Kit with Vertical Slice Architecture!"); - var info = new OpenApiInfo() - { - Title = "FSH.Starter.WebApi", - Version = description.ApiVersion.ToString(), - Contact = new OpenApiContact() { Name = "Mukesh Murugan", Email = "hello@codewithmukesh.com" } - }; - - if (description.IsDeprecated) - { - text.Append(" This API version has been deprecated."); - } - - info.Description = text.ToString(); - - return info; - } -} diff --git a/src/api/framework/Infrastructure/OpenApi/Extensions.cs b/src/api/framework/Infrastructure/OpenApi/Extensions.cs deleted file mode 100644 index df32e185fe..0000000000 --- a/src/api/framework/Infrastructure/OpenApi/Extensions.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using Swashbuckle.AspNetCore.SwaggerUI; - -namespace FSH.Framework.Infrastructure.OpenApi; - -public static class Extensions -{ - public static IServiceCollection ConfigureOpenApi(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - services.AddEndpointsApiExplorer(); - services.AddTransient, ConfigureSwaggerOptions>(); - services - .AddSwaggerGen(options => - { - options.OperationFilter(); - options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme - { - Type = SecuritySchemeType.Http, - Scheme = "bearer", - BearerFormat = "JWT", - Description = "JWT Authorization header using the Bearer scheme." - }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" } - }, - Array.Empty() - } - }); - }); - services - .AddApiVersioning(options => - { - options.ReportApiVersions = true; - options.DefaultApiVersion = new ApiVersion(1); - options.AssumeDefaultVersionWhenUnspecified = true; - options.ApiVersionReader = new UrlSegmentApiVersionReader(); - }) - .AddApiExplorer(options => - { - options.GroupNameFormat = "'v'VVV"; - }) - .EnableApiVersionBinding(); - return services; - } - public static WebApplication UseOpenApi(this WebApplication app) - { - ArgumentNullException.ThrowIfNull(app); - if (app.Environment.IsDevelopment() || app.Environment.EnvironmentName == "docker") - { - app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.DocExpansion(DocExpansion.None); - options.DisplayRequestDuration(); - - var swaggerEndpoints = app.DescribeApiVersions() - .Select(desc => new - { - Url = $"../swagger/{desc.GroupName}/swagger.json", - Name = desc.GroupName.ToUpperInvariant() - }); - - foreach (var endpoint in swaggerEndpoints) - { - options.SwaggerEndpoint(endpoint.Url, endpoint.Name); - } - }); - } - return app; - } -} diff --git a/src/api/framework/Infrastructure/OpenApi/SwaggerDefaultValues.cs b/src/api/framework/Infrastructure/OpenApi/SwaggerDefaultValues.cs deleted file mode 100644 index b872e69024..0000000000 --- a/src/api/framework/Infrastructure/OpenApi/SwaggerDefaultValues.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace FSH.Framework.Infrastructure.OpenApi; -public class SwaggerDefaultValues : IOperationFilter -{ - /// - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - ArgumentNullException.ThrowIfNull(operation); - ArgumentNullException.ThrowIfNull(context); - - var apiDescription = context.ApiDescription; - - operation.Deprecated |= apiDescription.IsDeprecated(); - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077 - foreach (var responseType in context.ApiDescription.SupportedResponseTypes) - { - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387 - var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString(); - var response = operation.Responses[responseKey]; - - foreach (var contentType in response.Content.Keys) - { - if (!responseType.ApiResponseFormats.Any(x => x.MediaType == contentType)) - { - response.Content.Remove(contentType); - } - } - } - - if (operation.Parameters == null) - { - return; - } - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - - parameter.Description ??= description.ModelMetadata?.Description; - - if (parameter.Schema.Default == null && - description.DefaultValue != null && - description.DefaultValue is not DBNull && - description.ModelMetadata is ModelMetadata modelMetadata) - { - // REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330 - var json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType); - parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json); - } - - parameter.Required |= description.IsRequired; - } - } -} diff --git a/src/api/framework/Infrastructure/Persistence/AppendGlobalQueryFilterExtension.cs b/src/api/framework/Infrastructure/Persistence/AppendGlobalQueryFilterExtension.cs deleted file mode 100644 index dbd09831d1..0000000000 --- a/src/api/framework/Infrastructure/Persistence/AppendGlobalQueryFilterExtension.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; - -namespace FSH.Framework.Infrastructure.Persistence; - -internal static class ModelBuilderExtensions -{ - public static ModelBuilder AppendGlobalQueryFilter(this ModelBuilder modelBuilder, Expression> filter) - { - // get a list of entities without a baseType that implement the interface TInterface - var entities = modelBuilder.Model.GetEntityTypes() - .Where(e => e.BaseType is null && e.ClrType.GetInterface(typeof(TInterface).Name) is not null) - .Select(e => e.ClrType); - - foreach (var entity in entities) - { - var parameterType = Expression.Parameter(modelBuilder.Entity(entity).Metadata.ClrType); - var filterBody = ReplacingExpressionVisitor.Replace(filter.Parameters.Single(), parameterType, filter.Body); - - // get the existing query filter - if (modelBuilder.Entity(entity).Metadata.GetQueryFilter() is { } existingFilter) - { - var existingFilterBody = ReplacingExpressionVisitor.Replace(existingFilter.Parameters.Single(), parameterType, existingFilter.Body); - - // combine the existing query filter with the new query filter - filterBody = Expression.AndAlso(existingFilterBody, filterBody); - } - - // apply the new query filter - modelBuilder.Entity(entity).HasQueryFilter(Expression.Lambda(filterBody, parameterType)); - } - - return modelBuilder; - } -} diff --git a/src/api/framework/Infrastructure/Persistence/DbProviders.cs b/src/api/framework/Infrastructure/Persistence/DbProviders.cs deleted file mode 100644 index f330df5123..0000000000 --- a/src/api/framework/Infrastructure/Persistence/DbProviders.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Framework.Infrastructure.Persistence; -internal static class DbProviders -{ - public const string PostgreSQL = "POSTGRESQL"; - public const string MSSQL = "MSSQL"; -} diff --git a/src/api/framework/Infrastructure/Persistence/Extensions.cs b/src/api/framework/Infrastructure/Persistence/Extensions.cs deleted file mode 100644 index dce8cb5a64..0000000000 --- a/src/api/framework/Infrastructure/Persistence/Extensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence.Interceptors; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Serilog; - -namespace FSH.Framework.Infrastructure.Persistence; -public static class Extensions -{ - private static readonly ILogger Logger = Log.ForContext(typeof(Extensions)); - internal static DbContextOptionsBuilder ConfigureDatabase(this DbContextOptionsBuilder builder, string dbProvider, string connectionString) - { - builder.ConfigureWarnings(warnings => warnings.Log(RelationalEventId.PendingModelChangesWarning)); - return dbProvider.ToUpperInvariant() switch - { - DbProviders.PostgreSQL => builder.UseNpgsql(connectionString, e => - e.MigrationsAssembly("FSH.Starter.WebApi.Migrations.PostgreSQL")).EnableSensitiveDataLogging(), - DbProviders.MSSQL => builder.UseSqlServer(connectionString, e => - e.MigrationsAssembly("FSH.Starter.WebApi.Migrations.MSSQL")), - _ => throw new InvalidOperationException($"DB Provider {dbProvider} is not supported."), - }; - } - - public static WebApplicationBuilder ConfigureDatabase(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - builder.Services.AddOptions() - .BindConfiguration(nameof(DatabaseOptions)) - .ValidateDataAnnotations() - .PostConfigure(config => - { - Logger.Information("current db provider: {DatabaseProvider}", config.Provider); - Logger.Information("for documentations and guides, visit https://www.fullstackhero.net"); - Logger.Information("to sponsor this project, visit https://opencollective.com/fullstackhero"); - }); - builder.Services.AddScoped(); - return builder; - } - - public static IServiceCollection BindDbContext(this IServiceCollection services) - where TContext : DbContext - { - ArgumentNullException.ThrowIfNull(services); - - services.AddDbContext((sp, options) => - { - var dbConfig = sp.GetRequiredService>().Value; - options.ConfigureDatabase(dbConfig.Provider, dbConfig.ConnectionString); - options.AddInterceptors(sp.GetServices()); - }); - return services; - } -} diff --git a/src/api/framework/Infrastructure/Persistence/FshDbContext.cs b/src/api/framework/Infrastructure/Persistence/FshDbContext.cs deleted file mode 100644 index 1f3186e3e5..0000000000 --- a/src/api/framework/Infrastructure/Persistence/FshDbContext.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using Finbuckle.MultiTenant.EntityFrameworkCore; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Tenant; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Persistence; -public class FshDbContext(IMultiTenantContextAccessor multiTenantContextAccessor, - DbContextOptions options, - IPublisher publisher, - IOptions settings) - : MultiTenantDbContext(multiTenantContextAccessor, options) -{ - private readonly IPublisher _publisher = publisher; - private readonly DatabaseOptions _settings = settings.Value; - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // QueryFilters need to be applied before base.OnModelCreating - modelBuilder.AppendGlobalQueryFilter(s => s.Deleted == null); - base.OnModelCreating(modelBuilder); - } - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.EnableSensitiveDataLogging(); - - if (!string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext.TenantInfo?.ConnectionString)) - { - optionsBuilder.ConfigureDatabase(_settings.Provider, multiTenantContextAccessor.MultiTenantContext.TenantInfo.ConnectionString!); - } - } - public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) - { - this.TenantNotSetMode = TenantNotSetMode.Overwrite; - int result = await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - await PublishDomainEventsAsync().ConfigureAwait(false); - return result; - } - private async Task PublishDomainEventsAsync() - { - var domainEvents = ChangeTracker.Entries() - .Select(e => e.Entity) - .Where(e => e.DomainEvents.Count > 0) - .SelectMany(e => - { - var domainEvents = e.DomainEvents.ToList(); - e.DomainEvents.Clear(); - return domainEvents; - }) - .ToList(); - - foreach (var domainEvent in domainEvents) - { - await _publisher.Publish(domainEvent).ConfigureAwait(false); - } - } -} diff --git a/src/api/framework/Infrastructure/Persistence/Interceptors/AuditInterceptor.cs b/src/api/framework/Infrastructure/Persistence/Interceptors/AuditInterceptor.cs deleted file mode 100644 index 6c2d819cac..0000000000 --- a/src/api/framework/Infrastructure/Persistence/Interceptors/AuditInterceptor.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System.Collections.ObjectModel; -using FSH.Framework.Core.Audit; -using FSH.Framework.Core.Domain; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Framework.Core.Identity.Users.Abstractions; -using FSH.Framework.Infrastructure.Identity.Audit; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Diagnostics; - -namespace FSH.Framework.Infrastructure.Persistence.Interceptors; -public class AuditInterceptor(ICurrentUser currentUser, TimeProvider timeProvider, IPublisher publisher) : SaveChangesInterceptor -{ - - public override ValueTask SavedChangesAsync(SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = default) - { - return base.SavedChangesAsync(eventData, result, cancellationToken); - } - - public override Task SaveChangesFailedAsync(DbContextErrorEventData eventData, CancellationToken cancellationToken = default) - { - return base.SaveChangesFailedAsync(eventData, cancellationToken); - } - - public override async ValueTask> SavingChangesAsync(DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) - { - UpdateEntities(eventData.Context); - await PublishAuditTrailsAsync(eventData); - return await base.SavingChangesAsync(eventData, result, cancellationToken); - } - - private async Task PublishAuditTrailsAsync(DbContextEventData eventData) - { - if (eventData.Context == null) return; - eventData.Context.ChangeTracker.DetectChanges(); - var trails = new List(); - var utcNow = timeProvider.GetUtcNow(); - foreach (var entry in eventData.Context.ChangeTracker.Entries().Where(x => x.State is EntityState.Added or EntityState.Deleted or EntityState.Modified).ToList()) - { - var userId = currentUser.GetUserId(); - var trail = new TrailDto() - { - Id = Guid.NewGuid(), - TableName = entry.Entity.GetType().Name, - UserId = userId, - DateTime = utcNow - }; - - foreach (var property in entry.Properties) - { - if (property.IsTemporary) - { - continue; - } - string propertyName = property.Metadata.Name; - if (property.Metadata.IsPrimaryKey()) - { - trail.KeyValues[propertyName] = property.CurrentValue; - continue; - } - - switch (entry.State) - { - case EntityState.Added: - trail.Type = TrailType.Create; - trail.NewValues[propertyName] = property.CurrentValue; - break; - - case EntityState.Deleted: - trail.Type = TrailType.Delete; - trail.OldValues[propertyName] = property.OriginalValue; - break; - - case EntityState.Modified: - if (property.IsModified) - { - if (entry.Entity is ISoftDeletable && property.OriginalValue == null && property.CurrentValue != null) - { - trail.ModifiedProperties.Add(propertyName); - trail.Type = TrailType.Delete; - trail.OldValues[propertyName] = property.OriginalValue; - trail.NewValues[propertyName] = property.CurrentValue; - } - else if (property.OriginalValue?.Equals(property.CurrentValue) == false) - { - trail.ModifiedProperties.Add(propertyName); - trail.Type = TrailType.Update; - trail.OldValues[propertyName] = property.OriginalValue; - trail.NewValues[propertyName] = property.CurrentValue; - } - else - { - property.IsModified = false; - } - } - break; - } - } - - trails.Add(trail); - } - if (trails.Count == 0) return; - var auditTrails = new Collection(); - foreach (var trail in trails) - { - auditTrails.Add(trail.ToAuditTrail()); - } - await publisher.Publish(new AuditPublishedEvent(auditTrails)); - } - - public void UpdateEntities(DbContext? context) - { - if (context == null) return; - foreach (var entry in context.ChangeTracker.Entries()) - { - var utcNow = timeProvider.GetUtcNow(); - if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities()) - { - if (entry.State == EntityState.Added) - { - entry.Entity.CreatedBy = currentUser.GetUserId(); - entry.Entity.Created = utcNow; - } - entry.Entity.LastModifiedBy = currentUser.GetUserId(); - entry.Entity.LastModified = utcNow; - } - if(entry.State is EntityState.Deleted && entry.Entity is ISoftDeletable softDelete) - { - softDelete.DeletedBy = currentUser.GetUserId(); - softDelete.Deleted = utcNow; - entry.State = EntityState.Modified; - } - } - } -} - -public static class Extensions -{ - public static bool HasChangedOwnedEntities(this EntityEntry entry) => - entry.References.Any(r => - r.TargetEntry != null && - r.TargetEntry.Metadata.IsOwned() && - (r.TargetEntry.State == EntityState.Added || r.TargetEntry.State == EntityState.Modified)); -} diff --git a/src/api/framework/Infrastructure/Persistence/Services/ConnectionStringValidator.cs b/src/api/framework/Infrastructure/Persistence/Services/ConnectionStringValidator.cs deleted file mode 100644 index 2b39de48d8..0000000000 --- a/src/api/framework/Infrastructure/Persistence/Services/ConnectionStringValidator.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FSH.Framework.Core.Persistence; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Npgsql; - -namespace FSH.Framework.Infrastructure.Persistence.Services; -internal sealed class ConnectionStringValidator(IOptions dbSettings, ILogger logger) : IConnectionStringValidator -{ - private readonly DatabaseOptions _dbSettings = dbSettings.Value; - private readonly ILogger _logger = logger; - - public bool TryValidate(string connectionString, string? dbProvider = null) - { - if (string.IsNullOrWhiteSpace(dbProvider)) - { - dbProvider = _dbSettings.Provider; - } - - try - { - switch (dbProvider?.ToUpperInvariant()) - { - case DbProviders.PostgreSQL: - _ = new NpgsqlConnectionStringBuilder(connectionString); - break; - case DbProviders.MSSQL: - _ = new SqlConnectionStringBuilder(connectionString); - break; - default: - break; - } - - return true; - } - catch (Exception ex) - { -#pragma warning disable S6667 // Logging in a catch clause should pass the caught exception as a parameter. - _logger.LogError("Connection String Validation Exception : {Error}", ex.Message); -#pragma warning restore S6667 // Logging in a catch clause should pass the caught exception as a parameter. - return false; - } - } -} diff --git a/src/api/framework/Infrastructure/RateLimit/Extensions.cs b/src/api/framework/Infrastructure/RateLimit/Extensions.cs deleted file mode 100644 index 3b00e7d85f..0000000000 --- a/src/api/framework/Infrastructure/RateLimit/Extensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Threading.RateLimiting; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.RateLimiting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.RateLimit; - -public static class Extensions -{ - internal static IServiceCollection ConfigureRateLimit(this IServiceCollection services, IConfiguration config) - { - services.Configure(config.GetSection(nameof(RateLimitOptions))); - - var options = config.GetSection(nameof(RateLimitOptions)).Get(); - if (options is { EnableRateLimiting: true }) - { - services.AddRateLimiter(rateLimitOptions => - { - rateLimitOptions.GlobalLimiter = PartitionedRateLimiter.Create(httpContext => - { - return RateLimitPartition.GetFixedWindowLimiter(partitionKey: httpContext.Request.Headers.Host.ToString(), - factory: _ => new FixedWindowRateLimiterOptions - { - PermitLimit = options.PermitLimit, - Window = TimeSpan.FromSeconds(options.WindowInSeconds) - }); - }); - - rateLimitOptions.RejectionStatusCode = options.RejectionStatusCode; - rateLimitOptions.OnRejected = async (context, token) => - { - var message = BuildRateLimitResponseMessage(context); - - await context.HttpContext.Response.WriteAsync(message, cancellationToken: token); - }; - }); - } - - return services; - } - - internal static IApplicationBuilder UseRateLimit(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetRequiredService>().Value; - - if (options.EnableRateLimiting) - { - app.UseRateLimiter(); - } - - return app; - } - - private static string BuildRateLimitResponseMessage(OnRejectedContext onRejectedContext) - { - var hostName = onRejectedContext.HttpContext.Request.Headers.Host.ToString(); - - return $"You have reached the maximum number of requests allowed for the address ({hostName})."; - } -} diff --git a/src/api/framework/Infrastructure/RateLimit/RateLimitOptions.cs b/src/api/framework/Infrastructure/RateLimit/RateLimitOptions.cs deleted file mode 100644 index 6fd364c5d1..0000000000 --- a/src/api/framework/Infrastructure/RateLimit/RateLimitOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Framework.Infrastructure.RateLimit; - -public class RateLimitOptions -{ - public bool EnableRateLimiting { get; init; } - public int PermitLimit { get; init; } - public int WindowInSeconds { get; init; } - public int RejectionStatusCode { get; init; } -} diff --git a/src/api/framework/Infrastructure/SecurityHeaders/Extensions.cs b/src/api/framework/Infrastructure/SecurityHeaders/Extensions.cs deleted file mode 100644 index 7d8ea168c7..0000000000 --- a/src/api/framework/Infrastructure/SecurityHeaders/Extensions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.SecurityHeaders; - -public static class Extensions -{ - internal static IServiceCollection ConfigureSecurityHeaders(this IServiceCollection services, IConfiguration config) - { - services.Configure(config.GetSection(nameof(SecurityHeaderOptions))); - - return services; - } - - internal static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetRequiredService>().Value; - - if (options.Enable) - { - app.Use(async (context, next) => - { - if (!context.Response.HasStarted) - { - if (!string.IsNullOrWhiteSpace(options.Headers.XFrameOptions)) - { - context.Response.Headers.XFrameOptions = options.Headers.XFrameOptions; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.XContentTypeOptions)) - { - context.Response.Headers.XContentTypeOptions = options.Headers.XContentTypeOptions; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.ReferrerPolicy)) - { - context.Response.Headers.Referer = options.Headers.ReferrerPolicy; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.PermissionsPolicy)) - { - context.Response.Headers["Permissions-Policy"] = options.Headers.PermissionsPolicy; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.XXSSProtection)) - { - context.Response.Headers.XXSSProtection = options.Headers.XXSSProtection; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.ContentSecurityPolicy)) - { - context.Response.Headers.ContentSecurityPolicy = options.Headers.ContentSecurityPolicy; - } - - if (!string.IsNullOrWhiteSpace(options.Headers.StrictTransportSecurity)) - { - context.Response.Headers.StrictTransportSecurity = options.Headers.StrictTransportSecurity; - } - } - - await next.Invoke(); - }); - } - - return app; - } -} diff --git a/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaderOptions.cs b/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaderOptions.cs deleted file mode 100644 index 4fac61a596..0000000000 --- a/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaderOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Framework.Infrastructure.SecurityHeaders; - -public class SecurityHeaderOptions -{ - public bool Enable { get; set; } - public SecurityHeaders Headers { get; set; } = default!; -} diff --git a/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaders.cs b/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaders.cs deleted file mode 100644 index 596d99a175..0000000000 --- a/src/api/framework/Infrastructure/SecurityHeaders/SecurityHeaders.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FSH.Framework.Infrastructure.SecurityHeaders; - -public class SecurityHeaders -{ - public string? XContentTypeOptions { get; set; } - public string? ReferrerPolicy { get; set; } - public string? XXSSProtection { get; set; } - public string? XFrameOptions { get; set; } - public string? ContentSecurityPolicy { get; set; } - public string? PermissionsPolicy { get; set; } - public string? StrictTransportSecurity { get; set; } -} diff --git a/src/api/framework/Infrastructure/Storage/Files/Extension.cs b/src/api/framework/Infrastructure/Storage/Files/Extension.cs deleted file mode 100644 index 6699f126d2..0000000000 --- a/src/api/framework/Infrastructure/Storage/Files/Extension.cs +++ /dev/null @@ -1,25 +0,0 @@ -using FSH.Framework.Core.Storage; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; - -namespace FSH.Framework.Infrastructure.Storage.Files; - -internal static class Extension -{ - internal static IServiceCollection ConfigureFileStorage(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - services.AddTransient(); - - return services; - } - - internal static IApplicationBuilder UseFileStorage(this IApplicationBuilder app) => - app.UseStaticFiles(new StaticFileOptions() - { - FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "Files")), - RequestPath = new PathString("/Files") - }); -} diff --git a/src/api/framework/Infrastructure/Storage/Files/LocalFileStorageService.cs b/src/api/framework/Infrastructure/Storage/Files/LocalFileStorageService.cs deleted file mode 100644 index 16b786da6f..0000000000 --- a/src/api/framework/Infrastructure/Storage/Files/LocalFileStorageService.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using FSH.Framework.Core.Origin; -using FSH.Framework.Core.Storage; -using FSH.Framework.Core.Storage.File; -using FSH.Framework.Core.Storage.File.Features; -using FSH.Framework.Infrastructure.Common.Extensions; -using Microsoft.Extensions.Options; -namespace FSH.Framework.Infrastructure.Storage.Files -{ - public class LocalFileStorageService(IOptions originSettings) : IStorageService - { - public async Task UploadAsync(FileUploadCommand? request, FileType supportedFileType, CancellationToken cancellationToken = default) - where T : class - { - if (request == null || request.Data == null) - { - return null!; - } - - if (request.Extension is null || !supportedFileType.GetDescriptionList().Contains(request.Extension.ToLower(System.Globalization.CultureInfo.CurrentCulture))) - throw new InvalidOperationException("File Format Not Supported."); - if (request.Name is null) - throw new InvalidOperationException("Name is required."); - - string base64Data = Regex.Match(request.Data, "data:image/(?.+?),(?.+)").Groups["data"].Value; - - var streamData = new MemoryStream(Convert.FromBase64String(base64Data)); - if (streamData.Length > 0) - { - string folder = typeof(T).Name; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - folder = folder.Replace(@"\", "/", StringComparison.Ordinal); - } - - string folderName = supportedFileType switch - { - FileType.Image => Path.Combine("assets", "images", folder), - _ => Path.Combine("assets", "others", folder), - }; - string pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName); - Directory.CreateDirectory(pathToSave); - - string fileName = request.Name.Trim('"'); - fileName = RemoveSpecialCharacters(fileName); - fileName = fileName.ReplaceWhitespace("-"); - fileName += request.Extension.Trim(); - string fullPath = Path.Combine(pathToSave, fileName); - string dbPath = Path.Combine(folderName, fileName); - if (File.Exists(dbPath)) - { - dbPath = NextAvailableFilename(dbPath); - fullPath = NextAvailableFilename(fullPath); - } - - using var stream = new FileStream(fullPath, FileMode.Create); - await streamData.CopyToAsync(stream, cancellationToken); - var path = dbPath.Replace("\\", "/", StringComparison.Ordinal); - var imageUri = new Uri(originSettings.Value.OriginUrl!, path); - return imageUri; - } - else - { - return null!; - } - } - - public static string RemoveSpecialCharacters(string str) - { - return Regex.Replace(str, "[^a-zA-Z0-9_.]+", string.Empty, RegexOptions.Compiled); - } - - public void Remove(Uri? path) - { - var pathString = path!.ToString(); - if (File.Exists(pathString)) - { - File.Delete(pathString); - } - } - - private const string NumberPattern = "-{0}"; - - private static string NextAvailableFilename(string path) - { - if (!File.Exists(path)) - { - return path; - } - - if (Path.HasExtension(path)) - { - return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path), StringComparison.Ordinal), NumberPattern)); - } - - return GetNextFilename(path + NumberPattern); - } - - private static string GetNextFilename(string pattern) - { - string tmp = string.Format(pattern, 1); - - if (!File.Exists(tmp)) - { - return tmp; - } - - int min = 1, max = 2; - - while (File.Exists(string.Format(pattern, max))) - { - min = max; - max *= 2; - } - - while (max != min + 1) - { - int pivot = (max + min) / 2; - if (File.Exists(string.Format(pattern, pivot))) - { - min = pivot; - } - else - { - max = pivot; - } - } - - return string.Format(pattern, max); - } - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Abstractions/IFshTenantInfo.cs b/src/api/framework/Infrastructure/Tenant/Abstractions/IFshTenantInfo.cs deleted file mode 100644 index 843820200f..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Abstractions/IFshTenantInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; - -namespace FSH.Framework.Infrastructure.Tenant.Abstractions; -public interface IFshTenantInfo : ITenantInfo -{ - string? ConnectionString { get; set; } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/ActivateTenantEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/ActivateTenantEndpoint.cs deleted file mode 100644 index 4f2f24f87b..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/ActivateTenantEndpoint.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.ActivateTenant; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class ActivateTenantEndpoint -{ - internal static RouteHandlerBuilder MapActivateTenantEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/{id}/activate", (ISender mediator, string id) => mediator.Send(new ActivateTenantCommand(id))) - .WithName(nameof(ActivateTenantEndpoint)) - .WithSummary("activate tenant") - .RequirePermission("Permissions.Tenants.Update") - .WithDescription("activate tenant"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/CreateTenantEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/CreateTenantEndpoint.cs deleted file mode 100644 index 51d8a9f6fd..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/CreateTenantEndpoint.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.CreateTenant; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class CreateTenantEndpoint -{ - internal static RouteHandlerBuilder MapRegisterTenantEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/", (CreateTenantCommand request, ISender mediator) => mediator.Send(request)) - .WithName(nameof(CreateTenantEndpoint)) - .WithSummary("creates a tenant") - .RequirePermission("Permissions.Tenants.Create") - .WithDescription("creates a tenant"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/DisableTenantEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/DisableTenantEndpoint.cs deleted file mode 100644 index 64ef613204..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/DisableTenantEndpoint.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.DisableTenant; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class DisableTenantEndpoint -{ - internal static RouteHandlerBuilder MapDisableTenantEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/{id}/deactivate", (ISender mediator, string id) => mediator.Send(new DisableTenantCommand(id))) - .WithName(nameof(DisableTenantEndpoint)) - .WithSummary("activate tenant") - .RequirePermission("Permissions.Tenants.Update") - .WithDescription("activate tenant"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/Extensions.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/Extensions.cs deleted file mode 100644 index bc88511001..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/Extensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class Extensions -{ - public static IEndpointRouteBuilder MapTenantEndpoints(this IEndpointRouteBuilder app) - { - var tenantGroup = app.MapGroup("api/tenants").WithTags("tenants"); - tenantGroup.MapRegisterTenantEndpoint(); - tenantGroup.MapGetTenantsEndpoint(); - tenantGroup.MapGetTenantByIdEndpoint(); - tenantGroup.MapUpgradeTenantSubscriptionEndpoint(); - tenantGroup.MapActivateTenantEndpoint(); - tenantGroup.MapDisableTenantEndpoint(); - return app; - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantByIdEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantByIdEndpoint.cs deleted file mode 100644 index a429ac62e4..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantByIdEndpoint.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.GetTenantById; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class GetTenantByIdEndpoint -{ - internal static RouteHandlerBuilder MapGetTenantByIdEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id}", (ISender mediator, string id) => mediator.Send(new GetTenantByIdQuery(id))) - .WithName(nameof(GetTenantByIdEndpoint)) - .WithSummary("get tenant by id") - .RequirePermission("Permissions.Tenants.View") - .WithDescription("get tenant by id"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantsEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantsEndpoint.cs deleted file mode 100644 index 1bf590deb4..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/GetTenantsEndpoint.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.GetTenants; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; -public static class GetTenantsEndpoint -{ - internal static RouteHandlerBuilder MapGetTenantsEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/", (ISender mediator) => mediator.Send(new GetTenantsQuery())) - .WithName(nameof(GetTenantsEndpoint)) - .WithSummary("get tenants") - .RequirePermission("Permissions.Tenants.View") - .WithDescription("get tenants"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Endpoints/UpgradeSubscriptionEndpoint.cs b/src/api/framework/Infrastructure/Tenant/Endpoints/UpgradeSubscriptionEndpoint.cs deleted file mode 100644 index 182330544f..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Endpoints/UpgradeSubscriptionEndpoint.cs +++ /dev/null @@ -1,20 +0,0 @@ -using FSH.Framework.Core.Tenant.Features.UpgradeSubscription; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Framework.Infrastructure.Tenant.Endpoints; - -public static class UpgradeSubscriptionEndpoint -{ - internal static RouteHandlerBuilder MapUpgradeTenantSubscriptionEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/upgrade", (UpgradeSubscriptionCommand command, ISender mediator) => mediator.Send(command)) - .WithName(nameof(UpgradeSubscriptionEndpoint)) - .WithSummary("upgrade tenant subscription") - .RequirePermission("Permissions.Tenants.Update") - .WithDescription("upgrade tenant subscription"); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Extensions.cs b/src/api/framework/Infrastructure/Tenant/Extensions.cs deleted file mode 100644 index f7fea460cd..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Extensions.cs +++ /dev/null @@ -1,136 +0,0 @@ -using Finbuckle.MultiTenant; -using Finbuckle.MultiTenant.Abstractions; -using Finbuckle.MultiTenant.Stores.DistributedCacheStore; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Tenant.Abstractions; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Framework.Infrastructure.Persistence.Services; -using FSH.Framework.Infrastructure.Tenant.Persistence; -using FSH.Framework.Infrastructure.Tenant.Services; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Serilog; - -namespace FSH.Framework.Infrastructure.Tenant; -internal static class Extensions -{ - public static IServiceCollection ConfigureMultitenancy(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - services.AddTransient(); - services.BindDbContext(); - services - .AddMultiTenant(config => - { - // to save database calls to resolve tenant - // this was happening for every request earlier, leading to ineffeciency - config.Events.OnTenantResolveCompleted = async (context) => - { - if (context.MultiTenantContext.StoreInfo is null) return; - if (context.MultiTenantContext.StoreInfo.StoreType != typeof(DistributedCacheStore)) - { - var sp = ((HttpContext)context.Context!).RequestServices; - var distributedCacheStore = sp - .GetService>>()! - .FirstOrDefault(s => s.GetType() == typeof(DistributedCacheStore)); - - await distributedCacheStore!.TryAddAsync(context.MultiTenantContext.TenantInfo!); - } - await Task.FromResult(0); - }; - }) - .WithClaimStrategy(FshClaims.Tenant) - .WithHeaderStrategy(TenantConstants.Identifier) - .WithDelegateStrategy(async context => - { - if (context is not HttpContext httpContext) - return null; - if (!httpContext.Request.Query.TryGetValue("tenant", out var tenantIdentifier) || string.IsNullOrEmpty(tenantIdentifier)) - return null; - return await Task.FromResult(tenantIdentifier.ToString()); - }) - .WithDistributedCacheStore(TimeSpan.FromMinutes(60)) - .WithEFCoreStore(); - services.AddScoped(); - return services; - } - - public static WebApplication UseMultitenancy(this WebApplication app) - { - ArgumentNullException.ThrowIfNull(app); - app.UseMultiTenant(); - - // set up tenant store - var tenants = TenantStoreSetup(app); - - // set up tenant databases - app.SetupTenantDatabases(tenants); - - return app; - } - - private static IApplicationBuilder SetupTenantDatabases(this IApplicationBuilder app, IEnumerable tenants) - { - foreach (var tenant in tenants) - { - // create a scope for tenant - using var tenantScope = app.ApplicationServices.CreateScope(); - - //set current tenant so that the right connection string is used - tenantScope.ServiceProvider.GetRequiredService() - .MultiTenantContext = new MultiTenantContext() - { - TenantInfo = tenant - }; - - // using the scope, perform migrations / seeding - var initializers = tenantScope.ServiceProvider.GetServices(); - foreach (var initializer in initializers) - { - initializer.MigrateAsync(CancellationToken.None).Wait(); - initializer.SeedAsync(CancellationToken.None).Wait(); - } - } - return app; - } - - private static IEnumerable TenantStoreSetup(IApplicationBuilder app) - { - var scope = app.ApplicationServices.CreateScope(); - - // tenant master schema migration - var tenantDbContext = scope.ServiceProvider.GetRequiredService(); - if (tenantDbContext.Database.GetPendingMigrations().Any()) - { - tenantDbContext.Database.Migrate(); - Log.Information("applied database migrations for tenant module"); - } - - // default tenant seeding - if (tenantDbContext.TenantInfo.Find(TenantConstants.Root.Id) is null) - { - var rootTenant = new FshTenantInfo( - TenantConstants.Root.Id, - TenantConstants.Root.Name, - string.Empty, - TenantConstants.Root.EmailAddress); - - rootTenant.SetValidity(DateTime.UtcNow.AddYears(1)); - tenantDbContext.TenantInfo.Add(rootTenant); - tenantDbContext.SaveChanges(); - Log.Information("configured default tenant data"); - } - - // get all tenants from store - var tenantStore = scope.ServiceProvider.GetRequiredService>(); - var tenants = tenantStore.GetAllAsync().Result; - - //dispose scope - scope.Dispose(); - - return tenants; - } -} diff --git a/src/api/framework/Infrastructure/Tenant/FshTenantInfo.cs b/src/api/framework/Infrastructure/Tenant/FshTenantInfo.cs deleted file mode 100644 index 7be7d62d3e..0000000000 --- a/src/api/framework/Infrastructure/Tenant/FshTenantInfo.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Infrastructure.Tenant.Abstractions; -using FSH.Starter.Shared.Authorization; - -namespace FSH.Framework.Infrastructure.Tenant; -public sealed class FshTenantInfo : IFshTenantInfo -{ - public FshTenantInfo() - { - } - - public FshTenantInfo(string id, string name, string? connectionString, string adminEmail, string? issuer = null) - { - Id = id; - Identifier = id; - Name = name; - ConnectionString = connectionString ?? string.Empty; - AdminEmail = adminEmail; - IsActive = true; - Issuer = issuer; - - // Add Default 1 Month Validity for all new tenants. Something like a DEMO period for tenants. - ValidUpto = DateTime.UtcNow.AddMonths(1); - } - public string Id { get; set; } = default!; - public string Identifier { get; set; } = default!; - - public string Name { get; set; } = default!; - public string ConnectionString { get; set; } = default!; - - public string AdminEmail { get; set; } = default!; - public bool IsActive { get; set; } - public DateTime ValidUpto { get; set; } - public string? Issuer { get; set; } - - public void AddValidity(int months) => - ValidUpto = ValidUpto.AddMonths(months); - - public void SetValidity(in DateTime validTill) => - ValidUpto = ValidUpto < validTill - ? validTill - : throw new FshException("Subscription cannot be backdated."); - - public void Activate() - { - if (Id == TenantConstants.Root.Id) - { - throw new InvalidOperationException("Invalid Tenant"); - } - - IsActive = true; - } - - public void Deactivate() - { - if (Id == TenantConstants.Root.Id) - { - throw new InvalidOperationException("Invalid Tenant"); - } - - IsActive = false; - } - string? ITenantInfo.Id { get => Id; set => Id = value ?? throw new InvalidOperationException("Id can't be null."); } - string? ITenantInfo.Identifier { get => Identifier; set => Identifier = value ?? throw new InvalidOperationException("Identifier can't be null."); } - string? ITenantInfo.Name { get => Name; set => Name = value ?? throw new InvalidOperationException("Name can't be null."); } - string? IFshTenantInfo.ConnectionString { get => ConnectionString; set => ConnectionString = value ?? throw new InvalidOperationException("ConnectionString can't be null."); } -} diff --git a/src/api/framework/Infrastructure/Tenant/Persistence/TenantDbContext.cs b/src/api/framework/Infrastructure/Tenant/Persistence/TenantDbContext.cs deleted file mode 100644 index d778a7ce59..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Persistence/TenantDbContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Finbuckle.MultiTenant.EntityFrameworkCore.Stores.EFCoreStore; -using Microsoft.EntityFrameworkCore; - -namespace FSH.Framework.Infrastructure.Tenant.Persistence; -public class TenantDbContext : EFCoreStoreDbContext -{ - public const string Schema = "tenant"; - public TenantDbContext(DbContextOptions options) - : base(options) - { - AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - ArgumentNullException.ThrowIfNull(modelBuilder); - - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity().ToTable("Tenants", Schema); - } -} diff --git a/src/api/framework/Infrastructure/Tenant/Services/TenantService.cs b/src/api/framework/Infrastructure/Tenant/Services/TenantService.cs deleted file mode 100644 index bfc8458cf4..0000000000 --- a/src/api/framework/Infrastructure/Tenant/Services/TenantService.cs +++ /dev/null @@ -1,120 +0,0 @@ -using Finbuckle.MultiTenant; -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Exceptions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Tenant.Abstractions; -using FSH.Framework.Core.Tenant.Dtos; -using FSH.Framework.Core.Tenant.Features.CreateTenant; -using Mapster; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace FSH.Framework.Infrastructure.Tenant.Services; - -public sealed class TenantService : ITenantService -{ - private readonly IMultiTenantStore _tenantStore; - private readonly DatabaseOptions _config; - private readonly IServiceProvider _serviceProvider; - - public TenantService(IMultiTenantStore tenantStore, IOptions config, IServiceProvider serviceProvider) - { - _tenantStore = tenantStore; - _config = config.Value; - _serviceProvider = serviceProvider; - } - - public async Task ActivateAsync(string id, CancellationToken cancellationToken) - { - var tenant = await GetTenantInfoAsync(id).ConfigureAwait(false); - - if (tenant.IsActive) - { - throw new FshException($"tenant {id} is already activated"); - } - - tenant.Activate(); - - await _tenantStore.TryUpdateAsync(tenant).ConfigureAwait(false); - - return $"tenant {id} is now activated"; - } - - public async Task CreateAsync(CreateTenantCommand request, CancellationToken cancellationToken) - { - var connectionString = request.ConnectionString; - if (request.ConnectionString?.Trim() == _config.ConnectionString.Trim()) - { - connectionString = string.Empty; - } - - FshTenantInfo tenant = new(request.Id, request.Name, connectionString, request.AdminEmail, request.Issuer); - await _tenantStore.TryAddAsync(tenant).ConfigureAwait(false); - - await InitializeDatabase(tenant).ConfigureAwait(false); - - return tenant.Id; - } - - private async Task InitializeDatabase(FshTenantInfo tenant) - { - // First create a new scope - using var scope = _serviceProvider.CreateScope(); - - // Then set current tenant so the right connection string is used - scope.ServiceProvider.GetRequiredService() - .MultiTenantContext = new MultiTenantContext() - { - TenantInfo = tenant - }; - - // using the scope, perform migrations / seeding - var initializers = scope.ServiceProvider.GetServices(); - foreach (var initializer in initializers) - { - await initializer.MigrateAsync(CancellationToken.None).ConfigureAwait(false); - await initializer.SeedAsync(CancellationToken.None).ConfigureAwait(false); - } - } - - public async Task DeactivateAsync(string id) - { - var tenant = await GetTenantInfoAsync(id).ConfigureAwait(false); - if (!tenant.IsActive) - { - throw new FshException($"tenant {id} is already deactivated"); - } - - tenant.Deactivate(); - await _tenantStore.TryUpdateAsync(tenant).ConfigureAwait(false); - return $"tenant {id} is now deactivated"; - } - - public async Task ExistsWithIdAsync(string id) => - await _tenantStore.TryGetAsync(id).ConfigureAwait(false) is not null; - - public async Task ExistsWithNameAsync(string name) => - (await _tenantStore.GetAllAsync().ConfigureAwait(false)).Any(t => t.Name == name); - - public async Task> GetAllAsync() - { - var tenants = (await _tenantStore.GetAllAsync().ConfigureAwait(false)).Adapt>(); - return tenants; - } - - public async Task GetByIdAsync(string id) => - (await GetTenantInfoAsync(id).ConfigureAwait(false)) - .Adapt(); - - public async Task UpgradeSubscription(string id, DateTime extendedExpiryDate) - { - var tenant = await GetTenantInfoAsync(id).ConfigureAwait(false); - tenant.SetValidity(extendedExpiryDate); - await _tenantStore.TryUpdateAsync(tenant).ConfigureAwait(false); - return tenant.ValidUpto; - } - - private async Task GetTenantInfoAsync(string id) => - await _tenantStore.TryGetAsync(id).ConfigureAwait(false) - ?? throw new NotFoundException($"{typeof(FshTenantInfo).Name} {id} Not Found."); -} diff --git a/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.Designer.cs b/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.Designer.cs deleted file mode 100644 index ee52283a6f..0000000000 --- a/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.Designer.cs +++ /dev/null @@ -1,138 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Catalog -{ - [DbContext(typeof(CatalogDbContext))] - [Migration("20241123030623_Add Catalog Schema")] - partial class AddCatalogSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("catalog") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Brand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.ToTable("Brands", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("BrandId") - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Price") - .HasColumnType("decimal(18,2)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("BrandId"); - - b.ToTable("Products", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.HasOne("FSH.Starter.WebApi.Catalog.Domain.Brand", "Brand") - .WithMany() - .HasForeignKey("BrandId"); - - b.Navigation("Brand"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.cs b/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.cs deleted file mode 100644 index 419d491abb..0000000000 --- a/src/api/migrations/MSSQL/Catalog/20241123030623_Add Catalog Schema.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Catalog -{ - /// - public partial class AddCatalogSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "catalog"); - - migrationBuilder.CreateTable( - name: "Brands", - schema: "catalog", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "datetimeoffset", nullable: false), - CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), - LastModified = table.Column(type: "datetimeoffset", nullable: false), - LastModifiedBy = table.Column(type: "uniqueidentifier", nullable: true), - Deleted = table.Column(type: "datetimeoffset", nullable: true), - DeletedBy = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Brands", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Products", - schema: "catalog", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), - Price = table.Column(type: "decimal(18,2)", nullable: false), - BrandId = table.Column(type: "uniqueidentifier", nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "datetimeoffset", nullable: false), - CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), - LastModified = table.Column(type: "datetimeoffset", nullable: false), - LastModifiedBy = table.Column(type: "uniqueidentifier", nullable: true), - Deleted = table.Column(type: "datetimeoffset", nullable: true), - DeletedBy = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Products", x => x.Id); - table.ForeignKey( - name: "FK_Products_Brands_BrandId", - column: x => x.BrandId, - principalSchema: "catalog", - principalTable: "Brands", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Products_BrandId", - schema: "catalog", - table: "Products", - column: "BrandId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Products", - schema: "catalog"); - - migrationBuilder.DropTable( - name: "Brands", - schema: "catalog"); - } - } -} diff --git a/src/api/migrations/MSSQL/Catalog/CatalogDbContextModelSnapshot.cs b/src/api/migrations/MSSQL/Catalog/CatalogDbContextModelSnapshot.cs deleted file mode 100644 index df0564c347..0000000000 --- a/src/api/migrations/MSSQL/Catalog/CatalogDbContextModelSnapshot.cs +++ /dev/null @@ -1,135 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Catalog -{ - [DbContext(typeof(CatalogDbContext))] - partial class CatalogDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("catalog") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Brand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.ToTable("Brands", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("BrandId") - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Price") - .HasColumnType("decimal(18,2)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("BrandId"); - - b.ToTable("Products", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.HasOne("FSH.Starter.WebApi.Catalog.Domain.Brand", "Brand") - .WithMany() - .HasForeignKey("BrandId"); - - b.Navigation("Brand"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.Designer.cs b/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.Designer.cs deleted file mode 100644 index 083e64b009..0000000000 --- a/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.Designer.cs +++ /dev/null @@ -1,401 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Identity.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Identity -{ - [DbContext(typeof(IdentityDbContext))] - [Migration("20241123030737_Add Identity Schema")] - partial class AddIdentitySchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Core.Audit.AuditTrail", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("DateTime") - .HasColumnType("datetimeoffset"); - - b.Property("Entity") - .HasColumnType("nvarchar(max)"); - - b.Property("ModifiedProperties") - .HasColumnType("nvarchar(max)"); - - b.Property("NewValues") - .HasColumnType("nvarchar(max)"); - - b.Property("Operation") - .HasColumnType("nvarchar(max)"); - - b.Property("PreviousValues") - .HasColumnType("nvarchar(max)"); - - b.Property("PrimaryKey") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.ToTable("AuditTrails", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedBy") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetimeoffset"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("RoleClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Roles.FshRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName", "TenantId") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("Roles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Users.FshUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("FirstName") - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("LastName") - .HasColumnType("nvarchar(max)"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ObjectId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("RefreshToken") - .HasColumnType("nvarchar(max)"); - - b.Property("RefreshTokenExpiryTime") - .HasColumnType("datetime2"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("Users", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("UserLogins", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("UserRoles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("UserTokens", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.cs b/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.cs deleted file mode 100644 index c51ef44a6c..0000000000 --- a/src/api/migrations/MSSQL/Identity/20241123030737_Add Identity Schema.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Identity -{ - /// - public partial class AddIdentitySchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "identity"); - - migrationBuilder.CreateTable( - name: "AuditTrails", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - Operation = table.Column(type: "nvarchar(max)", nullable: true), - Entity = table.Column(type: "nvarchar(max)", nullable: true), - DateTime = table.Column(type: "datetimeoffset", nullable: false), - PreviousValues = table.Column(type: "nvarchar(max)", nullable: true), - NewValues = table.Column(type: "nvarchar(max)", nullable: true), - ModifiedProperties = table.Column(type: "nvarchar(max)", nullable: true), - PrimaryKey = table.Column(type: "nvarchar(max)", nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AuditTrails", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Roles", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - Description = table.Column(type: "nvarchar(max)", nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Roles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Users", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - FirstName = table.Column(type: "nvarchar(max)", nullable: true), - LastName = table.Column(type: "nvarchar(max)", nullable: true), - ImageUrl = table.Column(type: "nvarchar(max)", nullable: true), - IsActive = table.Column(type: "bit", nullable: false), - RefreshToken = table.Column(type: "nvarchar(max)", nullable: true), - RefreshTokenExpiryTime = table.Column(type: "datetime2", nullable: false), - ObjectId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "RoleClaims", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), - CreatedOn = table.Column(type: "datetimeoffset", nullable: false), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - RoleId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_RoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_RoleClaims_Roles_RoleId", - column: x => x.RoleId, - principalSchema: "identity", - principalTable: "Roles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserClaims", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserClaims", x => x.Id); - table.ForeignKey( - name: "FK_UserClaims_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserLogins", - schema: "identity", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "nvarchar(450)", nullable: false), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_UserLogins_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserRoles", - schema: "identity", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - RoleId = table.Column(type: "nvarchar(450)", nullable: false), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_UserRoles_Roles_RoleId", - column: x => x.RoleId, - principalSchema: "identity", - principalTable: "Roles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_UserRoles_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserTokens", - schema: "identity", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_UserTokens_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_RoleClaims_RoleId", - schema: "identity", - table: "RoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - schema: "identity", - table: "Roles", - columns: new[] { "NormalizedName", "TenantId" }, - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_UserClaims_UserId", - schema: "identity", - table: "UserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_UserLogins_UserId", - schema: "identity", - table: "UserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_UserRoles_RoleId", - schema: "identity", - table: "UserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - schema: "identity", - table: "Users", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - schema: "identity", - table: "Users", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AuditTrails", - schema: "identity"); - - migrationBuilder.DropTable( - name: "RoleClaims", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserClaims", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserLogins", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserRoles", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserTokens", - schema: "identity"); - - migrationBuilder.DropTable( - name: "Roles", - schema: "identity"); - - migrationBuilder.DropTable( - name: "Users", - schema: "identity"); - } - } -} diff --git a/src/api/migrations/MSSQL/Identity/IdentityDbContextModelSnapshot.cs b/src/api/migrations/MSSQL/Identity/IdentityDbContextModelSnapshot.cs deleted file mode 100644 index b14e13f498..0000000000 --- a/src/api/migrations/MSSQL/Identity/IdentityDbContextModelSnapshot.cs +++ /dev/null @@ -1,398 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Identity.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Identity -{ - [DbContext(typeof(IdentityDbContext))] - partial class IdentityDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Core.Audit.AuditTrail", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("DateTime") - .HasColumnType("datetimeoffset"); - - b.Property("Entity") - .HasColumnType("nvarchar(max)"); - - b.Property("ModifiedProperties") - .HasColumnType("nvarchar(max)"); - - b.Property("NewValues") - .HasColumnType("nvarchar(max)"); - - b.Property("Operation") - .HasColumnType("nvarchar(max)"); - - b.Property("PreviousValues") - .HasColumnType("nvarchar(max)"); - - b.Property("PrimaryKey") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.ToTable("AuditTrails", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedBy") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetimeoffset"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("RoleClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Roles.FshRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName", "TenantId") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("Roles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Users.FshUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("FirstName") - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("LastName") - .HasColumnType("nvarchar(max)"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ObjectId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("RefreshToken") - .HasColumnType("nvarchar(max)"); - - b.Property("RefreshTokenExpiryTime") - .HasColumnType("datetime2"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("Users", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("UserLogins", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("UserRoles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("UserTokens", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/MSSQL.csproj b/src/api/migrations/MSSQL/MSSQL.csproj deleted file mode 100644 index adb542a166..0000000000 --- a/src/api/migrations/MSSQL/MSSQL.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - FSH.Starter.WebApi.Migrations.MSSQL - FSH.Starter.WebApi.Migrations.MSSQL - - - - - - - - - - - - - diff --git a/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.Designer.cs b/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.Designer.cs deleted file mode 100644 index 6c649f26d5..0000000000 --- a/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.Designer.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Tenant.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Tenant -{ - [DbContext(typeof(TenantDbContext))] - [Migration("20241123030647_Add Tenant Schema")] - partial class AddTenantSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Tenant.FshTenantInfo", b => - { - b.Property("Id") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("AdminEmail") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ConnectionString") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Issuer") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ValidUpto") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Identifier") - .IsUnique(); - - b.ToTable("Tenants", "tenant"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.cs b/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.cs deleted file mode 100644 index dda5acf677..0000000000 --- a/src/api/migrations/MSSQL/Tenant/20241123030647_Add Tenant Schema.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Tenant -{ - /// - public partial class AddTenantSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "tenant"); - - migrationBuilder.CreateTable( - name: "Tenants", - schema: "tenant", - columns: table => new - { - Id = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Identifier = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(max)", nullable: false), - ConnectionString = table.Column(type: "nvarchar(max)", nullable: false), - AdminEmail = table.Column(type: "nvarchar(max)", nullable: false), - IsActive = table.Column(type: "bit", nullable: false), - ValidUpto = table.Column(type: "datetime2", nullable: false), - Issuer = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Tenants", x => x.Id); - }); - - migrationBuilder.CreateIndex( - name: "IX_Tenants_Identifier", - schema: "tenant", - table: "Tenants", - column: "Identifier", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Tenants", - schema: "tenant"); - } - } -} diff --git a/src/api/migrations/MSSQL/Tenant/TenantDbContextModelSnapshot.cs b/src/api/migrations/MSSQL/Tenant/TenantDbContextModelSnapshot.cs deleted file mode 100644 index 288a117987..0000000000 --- a/src/api/migrations/MSSQL/Tenant/TenantDbContextModelSnapshot.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Tenant.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Tenant -{ - [DbContext(typeof(TenantDbContext))] - partial class TenantDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Tenant.FshTenantInfo", b => - { - b.Property("Id") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("AdminEmail") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ConnectionString") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Issuer") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ValidUpto") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Identifier") - .IsUnique(); - - b.ToTable("Tenants", "tenant"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.Designer.cs b/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.Designer.cs deleted file mode 100644 index 505dd92c42..0000000000 --- a/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.Designer.cs +++ /dev/null @@ -1,75 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Todo.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Todo -{ - [DbContext(typeof(TodoDbContext))] - [Migration("20241123030700_Add Todo Schema")] - partial class AddTodoSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("todo") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Todo.Domain.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Title") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.ToTable("Todos", "todo"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.cs b/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.cs deleted file mode 100644 index 0f68d6be73..0000000000 --- a/src/api/migrations/MSSQL/Todo/20241123030700_Add Todo Schema.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Todo -{ - /// - public partial class AddTodoSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "todo"); - - migrationBuilder.CreateTable( - name: "Todos", - schema: "todo", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Title = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - Note = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), - TenantId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "datetimeoffset", nullable: false), - CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), - LastModified = table.Column(type: "datetimeoffset", nullable: false), - LastModifiedBy = table.Column(type: "uniqueidentifier", nullable: true), - Deleted = table.Column(type: "datetimeoffset", nullable: true), - DeletedBy = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Todos", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Todos", - schema: "todo"); - } - } -} diff --git a/src/api/migrations/MSSQL/Todo/TodoDbContextModelSnapshot.cs b/src/api/migrations/MSSQL/Todo/TodoDbContextModelSnapshot.cs deleted file mode 100644 index 59a159b6b6..0000000000 --- a/src/api/migrations/MSSQL/Todo/TodoDbContextModelSnapshot.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Todo.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.MSSQL.Todo -{ - [DbContext(typeof(TodoDbContext))] - partial class TodoDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("todo") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Todo.Domain.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Created") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Deleted") - .HasColumnType("datetimeoffset"); - - b.Property("DeletedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("LastModified") - .HasColumnType("datetimeoffset"); - - b.Property("LastModifiedBy") - .HasColumnType("uniqueidentifier"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Title") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.ToTable("Todos", "todo"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.Designer.cs b/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.Designer.cs deleted file mode 100644 index 58b695929c..0000000000 --- a/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.Designer.cs +++ /dev/null @@ -1,138 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Catalog -{ - [DbContext(typeof(CatalogDbContext))] - [Migration("20241123024839_Add Catalog Schema")] - partial class AddCatalogSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("catalog") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Brand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.ToTable("Brands", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("BrandId") - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Price") - .HasColumnType("numeric"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("BrandId"); - - b.ToTable("Products", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.HasOne("FSH.Starter.WebApi.Catalog.Domain.Brand", "Brand") - .WithMany() - .HasForeignKey("BrandId"); - - b.Navigation("Brand"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.cs b/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.cs deleted file mode 100644 index e31911ac56..0000000000 --- a/src/api/migrations/PostgreSQL/Catalog/20241123024839_Add Catalog Schema.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Catalog -{ - /// - public partial class AddCatalogSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "catalog"); - - migrationBuilder.CreateTable( - name: "Brands", - schema: "catalog", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "uuid", nullable: false), - LastModified = table.Column(type: "timestamp with time zone", nullable: false), - LastModifiedBy = table.Column(type: "uuid", nullable: true), - Deleted = table.Column(type: "timestamp with time zone", nullable: true), - DeletedBy = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Brands", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Products", - schema: "catalog", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - Price = table.Column(type: "numeric", nullable: false), - BrandId = table.Column(type: "uuid", nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "uuid", nullable: false), - LastModified = table.Column(type: "timestamp with time zone", nullable: false), - LastModifiedBy = table.Column(type: "uuid", nullable: true), - Deleted = table.Column(type: "timestamp with time zone", nullable: true), - DeletedBy = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Products", x => x.Id); - table.ForeignKey( - name: "FK_Products_Brands_BrandId", - column: x => x.BrandId, - principalSchema: "catalog", - principalTable: "Brands", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Products_BrandId", - schema: "catalog", - table: "Products", - column: "BrandId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Products", - schema: "catalog"); - - migrationBuilder.DropTable( - name: "Brands", - schema: "catalog"); - } - } -} diff --git a/src/api/migrations/PostgreSQL/Catalog/CatalogDbContextModelSnapshot.cs b/src/api/migrations/PostgreSQL/Catalog/CatalogDbContextModelSnapshot.cs deleted file mode 100644 index 66ec756fd7..0000000000 --- a/src/api/migrations/PostgreSQL/Catalog/CatalogDbContextModelSnapshot.cs +++ /dev/null @@ -1,135 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Catalog -{ - [DbContext(typeof(CatalogDbContext))] - partial class CatalogDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("catalog") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Brand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.ToTable("Brands", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("BrandId") - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Price") - .HasColumnType("numeric"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("BrandId"); - - b.ToTable("Products", "catalog"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Starter.WebApi.Catalog.Domain.Product", b => - { - b.HasOne("FSH.Starter.WebApi.Catalog.Domain.Brand", "Brand") - .WithMany() - .HasForeignKey("BrandId"); - - b.Navigation("Brand"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.Designer.cs b/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.Designer.cs deleted file mode 100644 index 6163907fac..0000000000 --- a/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.Designer.cs +++ /dev/null @@ -1,399 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Identity.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Identity -{ - [DbContext(typeof(IdentityDbContext))] - [Migration("20241123024818_Add Identity Schema")] - partial class AddIdentitySchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Core.Audit.AuditTrail", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("DateTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Entity") - .HasColumnType("text"); - - b.Property("ModifiedProperties") - .HasColumnType("text"); - - b.Property("NewValues") - .HasColumnType("text"); - - b.Property("Operation") - .HasColumnType("text"); - - b.Property("PreviousValues") - .HasColumnType("text"); - - b.Property("PrimaryKey") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.ToTable("AuditTrails", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("CreatedBy") - .HasColumnType("text"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("RoleClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Roles.FshRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName", "TenantId") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("Roles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Users.FshUser", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("FirstName") - .HasColumnType("text"); - - b.Property("ImageUrl") - .HasColumnType("text"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("LastName") - .HasColumnType("text"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ObjectId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("RefreshToken") - .HasColumnType("text"); - - b.Property("RefreshTokenExpiryTime") - .HasColumnType("timestamp with time zone"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("Users", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("UserLogins", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("UserRoles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("UserTokens", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.cs b/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.cs deleted file mode 100644 index 6487136614..0000000000 --- a/src/api/migrations/PostgreSQL/Identity/20241123024818_Add Identity Schema.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Identity -{ - /// - public partial class AddIdentitySchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "identity"); - - migrationBuilder.CreateTable( - name: "AuditTrails", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - UserId = table.Column(type: "uuid", nullable: false), - Operation = table.Column(type: "text", nullable: true), - Entity = table.Column(type: "text", nullable: true), - DateTime = table.Column(type: "timestamp with time zone", nullable: false), - PreviousValues = table.Column(type: "text", nullable: true), - NewValues = table.Column(type: "text", nullable: true), - ModifiedProperties = table.Column(type: "text", nullable: true), - PrimaryKey = table.Column(type: "text", nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AuditTrails", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Roles", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - Description = table.Column(type: "text", nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Roles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Users", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - FirstName = table.Column(type: "text", nullable: true), - LastName = table.Column(type: "text", nullable: true), - ImageUrl = table.Column(type: "text", nullable: true), - IsActive = table.Column(type: "boolean", nullable: false), - RefreshToken = table.Column(type: "text", nullable: true), - RefreshTokenExpiryTime = table.Column(type: "timestamp with time zone", nullable: false), - ObjectId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - PasswordHash = table.Column(type: "text", nullable: true), - SecurityStamp = table.Column(type: "text", nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - AccessFailedCount = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "RoleClaims", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - CreatedBy = table.Column(type: "text", nullable: true), - CreatedOn = table.Column(type: "timestamp with time zone", nullable: false), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - RoleId = table.Column(type: "text", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_RoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_RoleClaims_Roles_RoleId", - column: x => x.RoleId, - principalSchema: "identity", - principalTable: "Roles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserClaims", - schema: "identity", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "text", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserClaims", x => x.Id); - table.ForeignKey( - name: "FK_UserClaims_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserLogins", - schema: "identity", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "text", nullable: false), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_UserLogins_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserRoles", - schema: "identity", - columns: table => new - { - UserId = table.Column(type: "text", nullable: false), - RoleId = table.Column(type: "text", nullable: false), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_UserRoles_Roles_RoleId", - column: x => x.RoleId, - principalSchema: "identity", - principalTable: "Roles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_UserRoles_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "UserTokens", - schema: "identity", - columns: table => new - { - UserId = table.Column(type: "text", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_UserTokens_Users_UserId", - column: x => x.UserId, - principalSchema: "identity", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_RoleClaims_RoleId", - schema: "identity", - table: "RoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - schema: "identity", - table: "Roles", - columns: new[] { "NormalizedName", "TenantId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_UserClaims_UserId", - schema: "identity", - table: "UserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_UserLogins_UserId", - schema: "identity", - table: "UserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_UserRoles_RoleId", - schema: "identity", - table: "UserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - schema: "identity", - table: "Users", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - schema: "identity", - table: "Users", - column: "NormalizedUserName", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AuditTrails", - schema: "identity"); - - migrationBuilder.DropTable( - name: "RoleClaims", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserClaims", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserLogins", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserRoles", - schema: "identity"); - - migrationBuilder.DropTable( - name: "UserTokens", - schema: "identity"); - - migrationBuilder.DropTable( - name: "Roles", - schema: "identity"); - - migrationBuilder.DropTable( - name: "Users", - schema: "identity"); - } - } -} diff --git a/src/api/migrations/PostgreSQL/Identity/IdentityDbContextModelSnapshot.cs b/src/api/migrations/PostgreSQL/Identity/IdentityDbContextModelSnapshot.cs deleted file mode 100644 index 685f78d39e..0000000000 --- a/src/api/migrations/PostgreSQL/Identity/IdentityDbContextModelSnapshot.cs +++ /dev/null @@ -1,396 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Identity.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Identity -{ - [DbContext(typeof(IdentityDbContext))] - partial class IdentityDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Core.Audit.AuditTrail", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("DateTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Entity") - .HasColumnType("text"); - - b.Property("ModifiedProperties") - .HasColumnType("text"); - - b.Property("NewValues") - .HasColumnType("text"); - - b.Property("Operation") - .HasColumnType("text"); - - b.Property("PreviousValues") - .HasColumnType("text"); - - b.Property("PrimaryKey") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.ToTable("AuditTrails", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("CreatedBy") - .HasColumnType("text"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("RoleClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Roles.FshRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName", "TenantId") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("Roles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.Users.FshUser", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("FirstName") - .HasColumnType("text"); - - b.Property("ImageUrl") - .HasColumnType("text"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("LastName") - .HasColumnType("text"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ObjectId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("RefreshToken") - .HasColumnType("text"); - - b.Property("RefreshTokenExpiryTime") - .HasColumnType("timestamp with time zone"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("Users", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserClaims", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("UserLogins", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("UserRoles", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("UserTokens", "identity"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Identity.RoleClaims.FshRoleClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Roles.FshRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("FSH.Framework.Infrastructure.Identity.Users.FshUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/PostgreSQL.csproj b/src/api/migrations/PostgreSQL/PostgreSQL.csproj deleted file mode 100644 index 706f2aa206..0000000000 --- a/src/api/migrations/PostgreSQL/PostgreSQL.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - FSH.Starter.WebApi.Migrations.PostgreSQL - FSH.Starter.WebApi.Migrations.PostgreSQL - - - - - - - - - - - - - diff --git a/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.Designer.cs b/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.Designer.cs deleted file mode 100644 index cd4007fd06..0000000000 --- a/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.Designer.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Tenant.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Tenant -{ - [DbContext(typeof(TenantDbContext))] - [Migration("20241123024825_Add Tenant Schema")] - partial class AddTenantSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Tenant.FshTenantInfo", b => - { - b.Property("Id") - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("AdminEmail") - .IsRequired() - .HasColumnType("text"); - - b.Property("ConnectionString") - .IsRequired() - .HasColumnType("text"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("Issuer") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ValidUpto") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("Identifier") - .IsUnique(); - - b.ToTable("Tenants", "tenant"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.cs b/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.cs deleted file mode 100644 index 2b7a5dd93c..0000000000 --- a/src/api/migrations/PostgreSQL/Tenant/20241123024825_Add Tenant Schema.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Tenant -{ - /// - public partial class AddTenantSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "tenant"); - - migrationBuilder.CreateTable( - name: "Tenants", - schema: "tenant", - columns: table => new - { - Id = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - Identifier = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - ConnectionString = table.Column(type: "text", nullable: false), - AdminEmail = table.Column(type: "text", nullable: false), - IsActive = table.Column(type: "boolean", nullable: false), - ValidUpto = table.Column(type: "timestamp without time zone", nullable: false), - Issuer = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Tenants", x => x.Id); - }); - - migrationBuilder.CreateIndex( - name: "IX_Tenants_Identifier", - schema: "tenant", - table: "Tenants", - column: "Identifier", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Tenants", - schema: "tenant"); - } - } -} diff --git a/src/api/migrations/PostgreSQL/Tenant/TenantDbContextModelSnapshot.cs b/src/api/migrations/PostgreSQL/Tenant/TenantDbContextModelSnapshot.cs deleted file mode 100644 index 123e35b38e..0000000000 --- a/src/api/migrations/PostgreSQL/Tenant/TenantDbContextModelSnapshot.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -using System; -using FSH.Framework.Infrastructure.Tenant.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Tenant -{ - [DbContext(typeof(TenantDbContext))] - partial class TenantDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Framework.Infrastructure.Tenant.FshTenantInfo", b => - { - b.Property("Id") - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("AdminEmail") - .IsRequired() - .HasColumnType("text"); - - b.Property("ConnectionString") - .IsRequired() - .HasColumnType("text"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("Issuer") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ValidUpto") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("Identifier") - .IsUnique(); - - b.ToTable("Tenants", "tenant"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.Designer.cs b/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.Designer.cs deleted file mode 100644 index 249977d6fd..0000000000 --- a/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.Designer.cs +++ /dev/null @@ -1,75 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Todo.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Todo -{ - [DbContext(typeof(TodoDbContext))] - [Migration("20241123024832_Add Todo Schema")] - partial class AddTodoSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("todo") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Todo.Domain.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("Title") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.ToTable("Todos", "todo"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.cs b/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.cs deleted file mode 100644 index 15c5f75996..0000000000 --- a/src/api/migrations/PostgreSQL/Todo/20241123024832_Add Todo Schema.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Todo -{ - /// - public partial class AddTodoSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "todo"); - - migrationBuilder.CreateTable( - name: "Todos", - schema: "todo", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Title = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), - Note = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - TenantId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - Created = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "uuid", nullable: false), - LastModified = table.Column(type: "timestamp with time zone", nullable: false), - LastModifiedBy = table.Column(type: "uuid", nullable: true), - Deleted = table.Column(type: "timestamp with time zone", nullable: true), - DeletedBy = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Todos", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Todos", - schema: "todo"); - } - } -} diff --git a/src/api/migrations/PostgreSQL/Todo/TodoDbContextModelSnapshot.cs b/src/api/migrations/PostgreSQL/Todo/TodoDbContextModelSnapshot.cs deleted file mode 100644 index 4edd8aa7ec..0000000000 --- a/src/api/migrations/PostgreSQL/Todo/TodoDbContextModelSnapshot.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -using System; -using FSH.Starter.WebApi.Todo.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Todo -{ - [DbContext(typeof(TodoDbContext))] - partial class TodoDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("todo") - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FSH.Starter.WebApi.Todo.Domain.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasColumnType("uuid"); - - b.Property("Deleted") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedBy") - .HasColumnType("uuid"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LastModifiedBy") - .HasColumnType("uuid"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.Property("Title") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.ToTable("Todos", "todo"); - - b.HasAnnotation("Finbuckle:MultiTenant", true); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommand.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommand.cs deleted file mode 100644 index 0c2559f9aa..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.ComponentModel; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Create.v1; -public sealed record CreateBrandCommand( - [property: DefaultValue("Sample Brand")] string? Name, - [property: DefaultValue("Descriptive Description")] string? Description = null) : IRequest; - diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommandValidator.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommandValidator.cs deleted file mode 100644 index 0d2e695331..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandCommandValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Create.v1; -public class CreateBrandCommandValidator : AbstractValidator -{ - public CreateBrandCommandValidator() - { - RuleFor(b => b.Name).NotEmpty().MinimumLength(2).MaximumLength(100); - RuleFor(b => b.Description).MaximumLength(1000); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandHandler.cs deleted file mode 100644 index 15b4b7633d..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Create.v1; -public sealed class CreateBrandHandler( - ILogger logger, - [FromKeyedServices("catalog:brands")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(CreateBrandCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var brand = Brand.Create(request.Name!, request.Description); - await repository.AddAsync(brand, cancellationToken); - logger.LogInformation("brand created {BrandId}", brand.Id); - return new CreateBrandResponse(brand.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandResponse.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandResponse.cs deleted file mode 100644 index 11e63834bd..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Create/v1/CreateBrandResponse.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Create.v1; - -public sealed record CreateBrandResponse(Guid? Id); - diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandCommand.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandCommand.cs deleted file mode 100644 index 0e11b24149..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandCommand.cs +++ /dev/null @@ -1,5 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Delete.v1; -public sealed record DeleteBrandCommand( - Guid Id) : IRequest; diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandHandler.cs deleted file mode 100644 index d4afe86ef8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Delete/v1/DeleteBrandHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Delete.v1; -public sealed class DeleteBrandHandler( - ILogger logger, - [FromKeyedServices("catalog:brands")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(DeleteBrandCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var brand = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = brand ?? throw new BrandNotFoundException(request.Id); - await repository.DeleteAsync(brand, cancellationToken); - logger.LogInformation("Brand with id : {BrandId} deleted", brand.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/EventHandlers/BrandCreatedEventHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/EventHandlers/BrandCreatedEventHandler.cs deleted file mode 100644 index 777526b767..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/EventHandlers/BrandCreatedEventHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using FSH.Starter.WebApi.Catalog.Domain.Events; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.EventHandlers; - -public class BrandCreatedEventHandler(ILogger logger) : INotificationHandler -{ - public async Task Handle(BrandCreated notification, - CancellationToken cancellationToken) - { - logger.LogInformation("handling brand created domain event.."); - await Task.FromResult(notification); - logger.LogInformation("finished handling brand created domain event.."); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/BrandResponse.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/BrandResponse.cs deleted file mode 100644 index 726030b24e..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/BrandResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -public sealed record BrandResponse(Guid? Id, string Name, string? Description); diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandHandler.cs deleted file mode 100644 index 7848a10d62..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Caching; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -public sealed class GetBrandHandler( - [FromKeyedServices("catalog:brands")] IReadRepository repository, - ICacheService cache) - : IRequestHandler -{ - public async Task Handle(GetBrandRequest request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var item = await cache.GetOrSetAsync( - $"brand:{request.Id}", - async () => - { - var brandItem = await repository.GetByIdAsync(request.Id, cancellationToken); - if (brandItem == null) throw new BrandNotFoundException(request.Id); - return new BrandResponse(brandItem.Id, brandItem.Name, brandItem.Description); - }, - cancellationToken: cancellationToken); - return item!; - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandRequest.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandRequest.cs deleted file mode 100644 index a9354be5a8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Get/v1/GetBrandRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -public class GetBrandRequest : IRequest -{ - public Guid Id { get; set; } - public GetBrandRequest(Guid id) => Id = id; -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandSpecs.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandSpecs.cs deleted file mode 100644 index b18cadc7f9..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandSpecs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ardalis.Specification; -using FSH.Framework.Core.Paging; -using FSH.Framework.Core.Specifications; -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -using FSH.Starter.WebApi.Catalog.Domain; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Search.v1; -public class SearchBrandSpecs : EntitiesByPaginationFilterSpec -{ - public SearchBrandSpecs(SearchBrandsCommand command) - : base(command) => - Query - .OrderBy(c => c.Name, !command.HasOrderBy()) - .Where(b => b.Name.Contains(command.Keyword), !string.IsNullOrEmpty(command.Keyword)); -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsCommand.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsCommand.cs deleted file mode 100644 index 70f4b3e0a8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsCommand.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Search.v1; - -public class SearchBrandsCommand : PaginationFilter, IRequest> -{ - public string? Name { get; set; } - public string? Description { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsHandler.cs deleted file mode 100644 index 29b7107f40..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Search/v1/SearchBrandsHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Search.v1; -public sealed class SearchBrandsHandler( - [FromKeyedServices("catalog:brands")] IReadRepository repository) - : IRequestHandler> -{ - public async Task> Handle(SearchBrandsCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - - var spec = new SearchBrandSpecs(request); - - var items = await repository.ListAsync(spec, cancellationToken).ConfigureAwait(false); - var totalCount = await repository.CountAsync(spec, cancellationToken).ConfigureAwait(false); - - return new PagedList(items, request!.PageNumber, request!.PageSize, totalCount); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommand.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommand.cs deleted file mode 100644 index ce7dd54cf8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Update.v1; -public sealed record UpdateBrandCommand( - Guid Id, - string? Name, - string? Description = null) : IRequest; diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommandValidator.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommandValidator.cs deleted file mode 100644 index a3ce8da6cb..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandCommandValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Update.v1; -public class UpdateBrandCommandValidator : AbstractValidator -{ - public UpdateBrandCommandValidator() - { - RuleFor(b => b.Name).NotEmpty().MinimumLength(2).MaximumLength(100); - RuleFor(b => b.Description).MaximumLength(1000); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandHandler.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandHandler.cs deleted file mode 100644 index 2477fdb4ad..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Update.v1; -public sealed class UpdateBrandHandler( - ILogger logger, - [FromKeyedServices("catalog:brands")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(UpdateBrandCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var brand = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = brand ?? throw new BrandNotFoundException(request.Id); - var updatedBrand = brand.Update(request.Name, request.Description); - await repository.UpdateAsync(updatedBrand, cancellationToken); - logger.LogInformation("Brand with id : {BrandId} updated.", brand.Id); - return new UpdateBrandResponse(brand.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandResponse.cs b/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandResponse.cs deleted file mode 100644 index 6b4acdc870..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Brands/Update/v1/UpdateBrandResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application.Brands.Update.v1; -public sealed record UpdateBrandResponse(Guid? Id); diff --git a/src/api/modules/Catalog/Catalog.Application/Catalog.Application.csproj b/src/api/modules/Catalog/Catalog.Application/Catalog.Application.csproj deleted file mode 100644 index 34ba8437ef..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Catalog.Application.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - FSH.Starter.WebApi.Catalog.Application - FSH.Starter.WebApi.Catalog.Application - - - - - - diff --git a/src/api/modules/Catalog/Catalog.Application/CatalogMetadata.cs b/src/api/modules/Catalog/Catalog.Application/CatalogMetadata.cs deleted file mode 100644 index 0301ffd004..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/CatalogMetadata.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application; -public static class CatalogMetadata -{ - public static string Name { get; set; } = "CatalogApplication"; -} - diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommand.cs b/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommand.cs deleted file mode 100644 index 99291ae8e8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.ComponentModel; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Create.v1; -public sealed record CreateProductCommand( - [property: DefaultValue("Sample Product")] string? Name, - [property: DefaultValue(10)] decimal Price, - [property: DefaultValue("Descriptive Description")] string? Description = null, - [property: DefaultValue(null)] Guid? BrandId = null) : IRequest; diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommandValidator.cs b/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommandValidator.cs deleted file mode 100644 index 97e81f7599..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductCommandValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Create.v1; -public class CreateProductCommandValidator : AbstractValidator -{ - public CreateProductCommandValidator() - { - RuleFor(p => p.Name).NotEmpty().MinimumLength(2).MaximumLength(75); - RuleFor(p => p.Price).GreaterThan(0); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductHandler.cs deleted file mode 100644 index cb640ac642..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Create.v1; -public sealed class CreateProductHandler( - ILogger logger, - [FromKeyedServices("catalog:products")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(CreateProductCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var product = Product.Create(request.Name!, request.Description, request.Price, request.BrandId); - await repository.AddAsync(product, cancellationToken); - logger.LogInformation("product created {ProductId}", product.Id); - return new CreateProductResponse(product.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductResponse.cs b/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductResponse.cs deleted file mode 100644 index 2c97edab79..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Create/v1/CreateProductResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application.Products.Create.v1; -public sealed record CreateProductResponse(Guid? Id); diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductCommand.cs b/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductCommand.cs deleted file mode 100644 index 5119734346..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductCommand.cs +++ /dev/null @@ -1,5 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Delete.v1; -public sealed record DeleteProductCommand( - Guid Id) : IRequest; diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductHandler.cs deleted file mode 100644 index 182a282a4b..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Delete/v1/DeleteProductHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Delete.v1; -public sealed class DeleteProductHandler( - ILogger logger, - [FromKeyedServices("catalog:products")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(DeleteProductCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var product = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = product ?? throw new ProductNotFoundException(request.Id); - await repository.DeleteAsync(product, cancellationToken); - logger.LogInformation("product with id : {ProductId} deleted", product.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/EventHandlers/ProductCreatedEventHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/EventHandlers/ProductCreatedEventHandler.cs deleted file mode 100644 index 694616ab34..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/EventHandlers/ProductCreatedEventHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FSH.Starter.WebApi.Catalog.Domain.Events; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.EventHandlers; - -public class ProductCreatedEventHandler(ILogger logger) : INotificationHandler -{ - public async Task Handle(ProductCreated notification, - CancellationToken cancellationToken) - { - logger.LogInformation("handling product created domain event.."); - await Task.FromResult(notification); - logger.LogInformation("finished handling product created domain event.."); - } -} - diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductHandler.cs deleted file mode 100644 index 53f327e262..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Caching; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -public sealed class GetProductHandler( - [FromKeyedServices("catalog:products")] IReadRepository repository, - ICacheService cache) - : IRequestHandler -{ - public async Task Handle(GetProductRequest request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var item = await cache.GetOrSetAsync( - $"product:{request.Id}", - async () => - { - var spec = new GetProductSpecs(request.Id); - var productItem = await repository.FirstOrDefaultAsync(spec, cancellationToken); - if (productItem == null) throw new ProductNotFoundException(request.Id); - return productItem; - }, - cancellationToken: cancellationToken); - return item!; - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductRequest.cs b/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductRequest.cs deleted file mode 100644 index a85bd13fb1..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -public class GetProductRequest : IRequest -{ - public Guid Id { get; set; } - public GetProductRequest(Guid id) => Id = id; -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductSpecs.cs b/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductSpecs.cs deleted file mode 100644 index 9e30c3767b..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/GetProductSpecs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ardalis.Specification; -using FSH.Starter.WebApi.Catalog.Domain; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; - -public class GetProductSpecs : Specification -{ - public GetProductSpecs(Guid id) - { - Query - .Where(p => p.Id == id) - .Include(p => p.Brand); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/ProductResponse.cs b/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/ProductResponse.cs deleted file mode 100644 index 080d358685..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Get/v1/ProductResponse.cs +++ /dev/null @@ -1,4 +0,0 @@ -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -public sealed record ProductResponse(Guid? Id, string Name, string? Description, decimal Price, BrandResponse? Brand); diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductSpecs.cs b/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductSpecs.cs deleted file mode 100644 index 98567c6a5a..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductSpecs.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ardalis.Specification; -using FSH.Framework.Core.Paging; -using FSH.Framework.Core.Specifications; -using FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -using FSH.Starter.WebApi.Catalog.Domain; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Search.v1; -public class SearchProductSpecs : EntitiesByPaginationFilterSpec -{ - public SearchProductSpecs(SearchProductsCommand command) - : base(command) => - Query - .Include(p => p.Brand) - .OrderBy(c => c.Name, !command.HasOrderBy()) - .Where(p => p.BrandId == command.BrandId!.Value, command.BrandId.HasValue) - .Where(p => p.Price >= command.MinimumRate!.Value, command.MinimumRate.HasValue) - .Where(p => p.Price <= command.MaximumRate!.Value, command.MaximumRate.HasValue); -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsCommand.cs b/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsCommand.cs deleted file mode 100644 index ed19c2f958..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Search.v1; - -public class SearchProductsCommand : PaginationFilter, IRequest> -{ - public Guid? BrandId { get; set; } - public decimal? MinimumRate { get; set; } - public decimal? MaximumRate { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsHandler.cs deleted file mode 100644 index 7c6c290df0..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Search/v1/SearchProductsHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; - - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Search.v1; -public sealed class SearchProductsHandler( - [FromKeyedServices("catalog:products")] IReadRepository repository) - : IRequestHandler> -{ - public async Task> Handle(SearchProductsCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - - var spec = new SearchProductSpecs(request); - - var items = await repository.ListAsync(spec, cancellationToken).ConfigureAwait(false); - var totalCount = await repository.CountAsync(spec, cancellationToken).ConfigureAwait(false); - - return new PagedList(items, request!.PageNumber, request!.PageSize, totalCount); - } -} - diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommand.cs b/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommand.cs deleted file mode 100644 index dd7db751c0..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Update.v1; -public sealed record UpdateProductCommand( - Guid Id, - string? Name, - decimal Price, - string? Description = null, - Guid? BrandId = null) : IRequest; diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommandValidator.cs b/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommandValidator.cs deleted file mode 100644 index e0110ea84f..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductCommandValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Update.v1; -public class UpdateProductCommandValidator : AbstractValidator -{ - public UpdateProductCommandValidator() - { - RuleFor(p => p.Name).NotEmpty().MinimumLength(2).MaximumLength(75); - RuleFor(p => p.Price).GreaterThan(0); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductHandler.cs b/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductHandler.cs deleted file mode 100644 index 5062196250..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using FSH.Starter.WebApi.Catalog.Domain.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Application.Products.Update.v1; -public sealed class UpdateProductHandler( - ILogger logger, - [FromKeyedServices("catalog:products")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(UpdateProductCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var product = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = product ?? throw new ProductNotFoundException(request.Id); - var updatedProduct = product.Update(request.Name, request.Description, request.Price, request.BrandId); - await repository.UpdateAsync(updatedProduct, cancellationToken); - logger.LogInformation("product with id : {ProductId} updated.", product.Id); - return new UpdateProductResponse(product.Id); - } -} diff --git a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductResponse.cs b/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductResponse.cs deleted file mode 100644 index cf28a756c8..0000000000 --- a/src/api/modules/Catalog/Catalog.Application/Products/Update/v1/UpdateProductResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Catalog.Application.Products.Update.v1; -public sealed record UpdateProductResponse(Guid? Id); diff --git a/src/api/modules/Catalog/Catalog.Domain/Brand.cs b/src/api/modules/Catalog/Catalog.Domain/Brand.cs deleted file mode 100644 index 0d24695f54..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Brand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using FSH.Framework.Core.Domain; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Starter.WebApi.Catalog.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain; -public class Brand : AuditableEntity, IAggregateRoot -{ - public string Name { get; private set; } = string.Empty; - public string? Description { get; private set; } - - private Brand() { } - - private Brand(Guid id, string name, string? description) - { - Id = id; - Name = name; - Description = description; - QueueDomainEvent(new BrandCreated { Brand = this }); - } - - public static Brand Create(string name, string? description) - { - return new Brand(Guid.NewGuid(), name, description); - } - - public Brand Update(string? name, string? description) - { - bool isUpdated = false; - - if (!string.IsNullOrWhiteSpace(name) && !string.Equals(Name, name, StringComparison.OrdinalIgnoreCase)) - { - Name = name; - isUpdated = true; - } - - if (!string.Equals(Description, description, StringComparison.OrdinalIgnoreCase)) - { - Description = description; - isUpdated = true; - } - - if (isUpdated) - { - QueueDomainEvent(new BrandUpdated { Brand = this }); - } - - return this; - } -} - - diff --git a/src/api/modules/Catalog/Catalog.Domain/Catalog.Domain.csproj b/src/api/modules/Catalog/Catalog.Domain/Catalog.Domain.csproj deleted file mode 100644 index 96c37fb508..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Catalog.Domain.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - FSH.Starter.WebApi.Catalog.Domain - FSH.Starter.WebApi.Catalog.Domain - - - - - diff --git a/src/api/modules/Catalog/Catalog.Domain/Events/BrandCreated.cs b/src/api/modules/Catalog/Catalog.Domain/Events/BrandCreated.cs deleted file mode 100644 index a1ae4abeb1..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Events/BrandCreated.cs +++ /dev/null @@ -1,7 +0,0 @@ -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain.Events; -public sealed record BrandCreated : DomainEvent -{ - public Brand? Brand { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Events/BrandUpdated.cs b/src/api/modules/Catalog/Catalog.Domain/Events/BrandUpdated.cs deleted file mode 100644 index 4446dcf079..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Events/BrandUpdated.cs +++ /dev/null @@ -1,7 +0,0 @@ -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain.Events; -public sealed record BrandUpdated : DomainEvent -{ - public Brand? Brand { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Events/ProductCreated.cs b/src/api/modules/Catalog/Catalog.Domain/Events/ProductCreated.cs deleted file mode 100644 index a049fe6856..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Events/ProductCreated.cs +++ /dev/null @@ -1,7 +0,0 @@ -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain.Events; -public sealed record ProductCreated : DomainEvent -{ - public Product? Product { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Events/ProductUpdated.cs b/src/api/modules/Catalog/Catalog.Domain/Events/ProductUpdated.cs deleted file mode 100644 index ed38e3584a..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Events/ProductUpdated.cs +++ /dev/null @@ -1,7 +0,0 @@ -using FSH.Framework.Core.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain.Events; -public sealed record ProductUpdated : DomainEvent -{ - public Product? Product { get; set; } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Exceptions/BrandNotFoundException.cs b/src/api/modules/Catalog/Catalog.Domain/Exceptions/BrandNotFoundException.cs deleted file mode 100644 index 84a40a1b81..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Exceptions/BrandNotFoundException.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FSH.Framework.Core.Exceptions; - -namespace FSH.Starter.WebApi.Catalog.Domain.Exceptions; -public sealed class BrandNotFoundException : NotFoundException -{ - public BrandNotFoundException(Guid id) - : base($"brand with id {id} not found") - { - } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Exceptions/ProductNotFoundException.cs b/src/api/modules/Catalog/Catalog.Domain/Exceptions/ProductNotFoundException.cs deleted file mode 100644 index b3a3963a33..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Exceptions/ProductNotFoundException.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FSH.Framework.Core.Exceptions; - -namespace FSH.Starter.WebApi.Catalog.Domain.Exceptions; -public sealed class ProductNotFoundException : NotFoundException -{ - public ProductNotFoundException(Guid id) - : base($"product with id {id} not found") - { - } -} diff --git a/src/api/modules/Catalog/Catalog.Domain/Product.cs b/src/api/modules/Catalog/Catalog.Domain/Product.cs deleted file mode 100644 index e1ebd863ff..0000000000 --- a/src/api/modules/Catalog/Catalog.Domain/Product.cs +++ /dev/null @@ -1,68 +0,0 @@ -using FSH.Framework.Core.Domain; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Starter.WebApi.Catalog.Domain.Events; - -namespace FSH.Starter.WebApi.Catalog.Domain; -public class Product : AuditableEntity, IAggregateRoot -{ - public string Name { get; private set; } = string.Empty; - public string? Description { get; private set; } - public decimal Price { get; private set; } - public Guid? BrandId { get; private set; } - public virtual Brand Brand { get; private set; } = default!; - - private Product() { } - - private Product(Guid id, string name, string? description, decimal price, Guid? brandId) - { - Id = id; - Name = name; - Description = description; - Price = price; - BrandId = brandId; - - QueueDomainEvent(new ProductCreated { Product = this }); - } - - public static Product Create(string name, string? description, decimal price, Guid? brandId) - { - return new Product(Guid.NewGuid(), name, description, price, brandId); - } - - public Product Update(string? name, string? description, decimal? price, Guid? brandId) - { - bool isUpdated = false; - - if (!string.IsNullOrWhiteSpace(name) && !string.Equals(Name, name, StringComparison.OrdinalIgnoreCase)) - { - Name = name; - isUpdated = true; - } - - if (!string.Equals(Description, description, StringComparison.OrdinalIgnoreCase)) - { - Description = description; - isUpdated = true; - } - - if (price.HasValue && Price != price.Value) - { - Price = price.Value; - isUpdated = true; - } - - if (brandId.HasValue && brandId.Value != Guid.Empty && BrandId != brandId.Value) - { - BrandId = brandId.Value; - isUpdated = true; - } - - if (isUpdated) - { - QueueDomainEvent(new ProductUpdated { Product = this }); - } - - return this; - } -} - diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Catalog.Infrastructure.csproj b/src/api/modules/Catalog/Catalog.Infrastructure/Catalog.Infrastructure.csproj deleted file mode 100644 index 7607e1698f..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Catalog.Infrastructure.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - FSH.Starter.WebApi.Catalog.Infrastructure - FSH.Starter.WebApi.Catalog.Infrastructure - - - - - - - - - - - diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/CatalogModule.cs b/src/api/modules/Catalog/Catalog.Infrastructure/CatalogModule.cs deleted file mode 100644 index 8327a6a9a0..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/CatalogModule.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Carter; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -using FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure; -public static class CatalogModule -{ - public class Endpoints : CarterModule - { - public Endpoints() : base("catalog") { } - public override void AddRoutes(IEndpointRouteBuilder app) - { - var productGroup = app.MapGroup("products").WithTags("products"); - productGroup.MapProductCreationEndpoint(); - productGroup.MapGetProductEndpoint(); - productGroup.MapGetProductListEndpoint(); - productGroup.MapProductUpdateEndpoint(); - productGroup.MapProductDeleteEndpoint(); - - var brandGroup = app.MapGroup("brands").WithTags("brands"); - brandGroup.MapBrandCreationEndpoint(); - brandGroup.MapGetBrandEndpoint(); - brandGroup.MapGetBrandListEndpoint(); - brandGroup.MapBrandUpdateEndpoint(); - brandGroup.MapBrandDeleteEndpoint(); - } - } - public static WebApplicationBuilder RegisterCatalogServices(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - builder.Services.BindDbContext(); - builder.Services.AddScoped(); - builder.Services.AddKeyedScoped, CatalogRepository>("catalog:products"); - builder.Services.AddKeyedScoped, CatalogRepository>("catalog:products"); - builder.Services.AddKeyedScoped, CatalogRepository>("catalog:brands"); - builder.Services.AddKeyedScoped, CatalogRepository>("catalog:brands"); - return builder; - } - public static WebApplication UseCatalogModule(this WebApplication app) - { - return app; - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateBrandEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateBrandEndpoint.cs deleted file mode 100644 index b7adb6b9bf..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateBrandEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Brands.Create.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class CreateBrandEndpoint -{ - internal static RouteHandlerBuilder MapBrandCreationEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPost("/", async (CreateBrandCommand request, ISender mediator) => - { - var response = await mediator.Send(request); - return Results.Ok(response); - }) - .WithName(nameof(CreateBrandEndpoint)) - .WithSummary("creates a brand") - .WithDescription("creates a brand") - .Produces() - .RequirePermission("Permissions.Brands.Create") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateProductEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateProductEndpoint.cs deleted file mode 100644 index 1e018c0ed8..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/CreateProductEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Products.Create.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class CreateProductEndpoint -{ - internal static RouteHandlerBuilder MapProductCreationEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPost("/", async (CreateProductCommand request, ISender mediator) => - { - var response = await mediator.Send(request); - return Results.Ok(response); - }) - .WithName(nameof(CreateProductEndpoint)) - .WithSummary("creates a product") - .WithDescription("creates a product") - .Produces() - .RequirePermission("Permissions.Products.Create") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteBrandEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteBrandEndpoint.cs deleted file mode 100644 index 3b39820dce..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteBrandEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Brands.Delete.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class DeleteBrandEndpoint -{ - internal static RouteHandlerBuilder MapBrandDeleteEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapDelete("/{id:guid}", async (Guid id, ISender mediator) => - { - await mediator.Send(new DeleteBrandCommand(id)); - return Results.NoContent(); - }) - .WithName(nameof(DeleteBrandEndpoint)) - .WithSummary("deletes brand by id") - .WithDescription("deletes brand by id") - .Produces(StatusCodes.Status204NoContent) - .RequirePermission("Permissions.Brands.Delete") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteProductEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteProductEndpoint.cs deleted file mode 100644 index c5c25fce2a..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/DeleteProductEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Products.Delete.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class DeleteProductEndpoint -{ - internal static RouteHandlerBuilder MapProductDeleteEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapDelete("/{id:guid}", async (Guid id, ISender mediator) => - { - await mediator.Send(new DeleteProductCommand(id)); - return Results.NoContent(); - }) - .WithName(nameof(DeleteProductEndpoint)) - .WithSummary("deletes product by id") - .WithDescription("deletes product by id") - .Produces(StatusCodes.Status204NoContent) - .RequirePermission("Permissions.Products.Delete") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetBrandEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetBrandEndpoint.cs deleted file mode 100644 index 13600025c9..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetBrandEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class GetBrandEndpoint -{ - internal static RouteHandlerBuilder MapGetBrandEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapGet("/{id:guid}", async (Guid id, ISender mediator) => - { - var response = await mediator.Send(new GetBrandRequest(id)); - return Results.Ok(response); - }) - .WithName(nameof(GetBrandEndpoint)) - .WithSummary("gets brand by id") - .WithDescription("gets brand by id") - .Produces() - .RequirePermission("Permissions.Brands.View") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetProductEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetProductEndpoint.cs deleted file mode 100644 index 7fd15eb1f7..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/GetProductEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class GetProductEndpoint -{ - internal static RouteHandlerBuilder MapGetProductEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapGet("/{id:guid}", async (Guid id, ISender mediator) => - { - var response = await mediator.Send(new GetProductRequest(id)); - return Results.Ok(response); - }) - .WithName(nameof(GetProductEndpoint)) - .WithSummary("gets product by id") - .WithDescription("gets prodct by id") - .Produces() - .RequirePermission("Permissions.Products.View") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchBrandsEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchBrandsEndpoint.cs deleted file mode 100644 index bc6d9a83f0..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchBrandsEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Brands.Get.v1; -using FSH.Starter.WebApi.Catalog.Application.Brands.Search.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; - -public static class SearchBrandsEndpoint -{ - internal static RouteHandlerBuilder MapGetBrandListEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPost("/search", async (ISender mediator, [FromBody] SearchBrandsCommand command) => - { - var response = await mediator.Send(command); - return Results.Ok(response); - }) - .WithName(nameof(SearchBrandsEndpoint)) - .WithSummary("Gets a list of brands") - .WithDescription("Gets a list of brands with pagination and filtering support") - .Produces>() - .RequirePermission("Permissions.Brands.View") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchProductsEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchProductsEndpoint.cs deleted file mode 100644 index e3d058006b..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/SearchProductsEndpoint.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Products.Get.v1; -using FSH.Starter.WebApi.Catalog.Application.Products.Search.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; - -public static class SearchProductsEndpoint -{ - internal static RouteHandlerBuilder MapGetProductListEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPost("/search", async (ISender mediator, [FromBody] SearchProductsCommand command) => - { - var response = await mediator.Send(command); - return Results.Ok(response); - }) - .WithName(nameof(SearchProductsEndpoint)) - .WithSummary("Gets a list of products") - .WithDescription("Gets a list of products with pagination and filtering support") - .Produces>() - .RequirePermission("Permissions.Products.View") - .MapToApiVersion(1); - } -} - diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateBrandEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateBrandEndpoint.cs deleted file mode 100644 index 3e07b34bef..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateBrandEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Brands.Update.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class UpdateBrandEndpoint -{ - internal static RouteHandlerBuilder MapBrandUpdateEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPut("/{id:guid}", async (Guid id, UpdateBrandCommand request, ISender mediator) => - { - if (id != request.Id) return Results.BadRequest(); - var response = await mediator.Send(request); - return Results.Ok(response); - }) - .WithName(nameof(UpdateBrandEndpoint)) - .WithSummary("update a brand") - .WithDescription("update a brand") - .Produces() - .RequirePermission("Permissions.Brands.Update") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateProductEndpoint.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateProductEndpoint.cs deleted file mode 100644 index e3ee4f3e55..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Endpoints/v1/UpdateProductEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using FSH.Starter.WebApi.Catalog.Application.Products.Update.v1; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Endpoints.v1; -public static class UpdateProductEndpoint -{ - internal static RouteHandlerBuilder MapProductUpdateEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapPut("/{id:guid}", async (Guid id, UpdateProductCommand request, ISender mediator) => - { - if (id != request.Id) return Results.BadRequest(); - var response = await mediator.Send(request); - return Results.Ok(response); - }) - .WithName(nameof(UpdateProductEndpoint)) - .WithSummary("update a product") - .WithDescription("update a product") - .Produces() - .RequirePermission("Permissions.Products.Update") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbContext.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbContext.cs deleted file mode 100644 index dddec6be7f..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbContext.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.WebApi.Catalog.Domain; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Shared.Constants; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; - -public sealed class CatalogDbContext : FshDbContext -{ - public CatalogDbContext(IMultiTenantContextAccessor multiTenantContextAccessor, DbContextOptions options, IPublisher publisher, IOptions settings) - : base(multiTenantContextAccessor, options, publisher, settings) - { - } - - public DbSet Products { get; set; } = null!; - public DbSet Brands { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - ArgumentNullException.ThrowIfNull(modelBuilder); - base.OnModelCreating(modelBuilder); - modelBuilder.ApplyConfigurationsFromAssembly(typeof(CatalogDbContext).Assembly); - modelBuilder.HasDefaultSchema(SchemaNames.Catalog); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbInitializer.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbInitializer.cs deleted file mode 100644 index db213a0624..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogDbInitializer.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Catalog.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -internal sealed class CatalogDbInitializer( - ILogger logger, - CatalogDbContext context) : IDbInitializer -{ - public async Task MigrateAsync(CancellationToken cancellationToken) - { - if ((await context.Database.GetPendingMigrationsAsync(cancellationToken)).Any()) - { - await context.Database.MigrateAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("[{Tenant}] applied database migrations for catalog module", context.TenantInfo!.Identifier); - } - } - - public async Task SeedAsync(CancellationToken cancellationToken) - { - const string Name = "Keychron V6 QMK Custom Wired Mechanical Keyboard"; - const string Description = "A full-size layout QMK/VIA custom mechanical keyboard"; - const decimal Price = 79; - Guid? BrandId = null; - if (await context.Products.FirstOrDefaultAsync(t => t.Name == Name, cancellationToken).ConfigureAwait(false) is null) - { - var product = Product.Create(Name, Description, Price, BrandId); - await context.Products.AddAsync(product, cancellationToken); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("[{Tenant}] seeding default catalog data", context.TenantInfo!.Identifier); - } - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogRepository.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogRepository.cs deleted file mode 100644 index baa1292f08..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/CatalogRepository.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Ardalis.Specification; -using Ardalis.Specification.EntityFrameworkCore; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Framework.Core.Persistence; -using Mapster; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Persistence; -internal sealed class CatalogRepository : RepositoryBase, IReadRepository, IRepository - where T : class, IAggregateRoot -{ - public CatalogRepository(CatalogDbContext context) - : base(context) - { - } - - // We override the default behavior when mapping to a dto. - // We're using Mapster's ProjectToType here to immediately map the result from the database. - // This is only done when no Selector is defined, so regular specifications with a selector also still work. - protected override IQueryable ApplySpecification(ISpecification specification) => - specification.Selector is not null - ? base.ApplySpecification(specification) - : ApplySpecification(specification, false) - .ProjectToType(); -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/BrandConfigurations.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/BrandConfigurations.cs deleted file mode 100644 index 0abf96da30..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/BrandConfigurations.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Finbuckle.MultiTenant; -using FSH.Starter.WebApi.Catalog.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Persistence.Configurations; -internal sealed class BrandConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.IsMultiTenant(); - builder.HasKey(x => x.Id); - builder.Property(x => x.Name).HasMaxLength(100); - builder.Property(x => x.Description).HasMaxLength(1000); - } -} diff --git a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/ProductConfiguration.cs b/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/ProductConfiguration.cs deleted file mode 100644 index fc1cfffa92..0000000000 --- a/src/api/modules/Catalog/Catalog.Infrastructure/Persistence/Configurations/ProductConfiguration.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Finbuckle.MultiTenant; -using FSH.Starter.WebApi.Catalog.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace FSH.Starter.WebApi.Catalog.Infrastructure.Persistence.Configurations; -internal sealed class ProductConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.IsMultiTenant(); - builder.HasKey(x => x.Id); - builder.Property(x => x.Name).HasMaxLength(100); - builder.Property(x => x.Description).HasMaxLength(1000); - } -} diff --git a/src/api/modules/Catalog/CatalogModule.cs b/src/api/modules/Catalog/CatalogModule.cs deleted file mode 100644 index e90fd3c217..0000000000 --- a/src/api/modules/Catalog/CatalogModule.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Carter; -using FSH.WebApi.Modules.Catalog.Features.Products.ProductCreation.v1; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.WebApi.Modules.Catalog; - -public static class CatalogModule -{ - public class Endpoints : CarterModule - { - public Endpoints() : base("catalog") { } - public override void AddRoutes(IEndpointRouteBuilder app) - { - var productGroup = app.MapGroup("products").WithTags("products"); - productGroup.MapProductCreationEndpoint(); - - var testGroup = app.MapGroup("test").WithTags("test"); - testGroup.MapGet("/test", () => "hi"); - } - } - public static WebApplicationBuilder RegisterCatalogServices(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - return builder; - } - public static WebApplication UseCatalogModule(this WebApplication app) - { - return app; - } -} diff --git a/src/api/modules/Todo/Domain/Events/TodoItemCreated.cs b/src/api/modules/Todo/Domain/Events/TodoItemCreated.cs deleted file mode 100644 index 224e6de853..0000000000 --- a/src/api/modules/Todo/Domain/Events/TodoItemCreated.cs +++ /dev/null @@ -1,22 +0,0 @@ - -using FSH.Framework.Core.Caching; -using FSH.Framework.Core.Domain.Events; -using FSH.Starter.WebApi.Todo.Features.Get.v1; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Domain.Events; -public record TodoItemCreated(Guid Id, string Title, string Note) : DomainEvent; - -public class TodoItemCreatedEventHandler( - ILogger logger, - ICacheService cache) - : INotificationHandler -{ - public async Task Handle(TodoItemCreated notification, CancellationToken cancellationToken) - { - logger.LogInformation("handling todo item created domain event.."); - var cacheResponse = new GetTodoResponse(notification.Id, notification.Title, notification.Note); - await cache.SetAsync($"todo:{notification.Id}", cacheResponse, cancellationToken: cancellationToken); - } -} diff --git a/src/api/modules/Todo/Domain/Events/TodoItemUpdated.cs b/src/api/modules/Todo/Domain/Events/TodoItemUpdated.cs deleted file mode 100644 index c5a11e85b8..0000000000 --- a/src/api/modules/Todo/Domain/Events/TodoItemUpdated.cs +++ /dev/null @@ -1,22 +0,0 @@ - -using FSH.Framework.Core.Caching; -using FSH.Framework.Core.Domain.Events; -using FSH.Starter.WebApi.Todo.Features.Get.v1; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Domain.Events; -public record TodoItemUpdated(TodoItem item) : DomainEvent; - -public class TodoItemUpdatedEventHandler( - ILogger logger, - ICacheService cache) - : INotificationHandler -{ - public async Task Handle(TodoItemUpdated notification, CancellationToken cancellationToken) - { - logger.LogInformation("handling todo item update domain event.."); - var cacheResponse = new GetTodoResponse(notification.item.Id, notification.item.Title, notification.item.Note); - await cache.SetAsync($"todo:{notification.item.Id}", cacheResponse, cancellationToken: cancellationToken); - } -} diff --git a/src/api/modules/Todo/Domain/TodoItem.cs b/src/api/modules/Todo/Domain/TodoItem.cs deleted file mode 100644 index 0e32a2add5..0000000000 --- a/src/api/modules/Todo/Domain/TodoItem.cs +++ /dev/null @@ -1,46 +0,0 @@ -using FSH.Framework.Core.Domain; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Starter.WebApi.Todo.Domain.Events; - -namespace FSH.Starter.WebApi.Todo.Domain; -public sealed class TodoItem : AuditableEntity, IAggregateRoot -{ - public string Title { get; private set; } = string.Empty; - public string Note { get; private set; } = string.Empty; - - private TodoItem() { } - - private TodoItem(string title, string note) - { - Title = title; - Note = note; - QueueDomainEvent(new TodoItemCreated(Id, Title, Note)); - TodoMetrics.Created.Add(1); - } - - public static TodoItem Create(string title, string note) => new(title, note); - - public TodoItem Update(string? title, string? note) - { - bool isUpdated = false; - - if (!string.IsNullOrWhiteSpace(title) && !string.Equals(Title, title, StringComparison.OrdinalIgnoreCase)) - { - Title = title; - isUpdated = true; - } - - if (!string.IsNullOrWhiteSpace(note) && !string.Equals(Note, note, StringComparison.OrdinalIgnoreCase)) - { - Note = note; - isUpdated = true; - } - - if (isUpdated) - { - QueueDomainEvent(new TodoItemUpdated(this)); - } - - return this; - } -} diff --git a/src/api/modules/Todo/Domain/TodoMetrics.cs b/src/api/modules/Todo/Domain/TodoMetrics.cs deleted file mode 100644 index cdd98b8df6..0000000000 --- a/src/api/modules/Todo/Domain/TodoMetrics.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.Metrics; -using FSH.Starter.Aspire.ServiceDefaults; - -namespace FSH.Starter.WebApi.Todo.Domain; - -public static class TodoMetrics -{ - private static readonly Meter Meter = new Meter(MetricsConstants.Todos, "1.0.0"); - public static readonly Counter Created = Meter.CreateCounter("items.created"); - public static readonly Counter Updated = Meter.CreateCounter("items.updated"); - public static readonly Counter Deleted = Meter.CreateCounter("items.deleted"); -} - diff --git a/src/api/modules/Todo/Exceptions/TodoItemNotFoundException.cs b/src/api/modules/Todo/Exceptions/TodoItemNotFoundException.cs deleted file mode 100644 index 39ab54f701..0000000000 --- a/src/api/modules/Todo/Exceptions/TodoItemNotFoundException.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FSH.Framework.Core.Exceptions; - -namespace FSH.Starter.WebApi.Todo.Exceptions; -internal sealed class TodoItemNotFoundException : NotFoundException -{ - public TodoItemNotFoundException(Guid id) - : base($"todo item with id {id} not found") - { - } -} diff --git a/src/api/modules/Todo/Features/Create/v1/CreateTodoCommand.cs b/src/api/modules/Todo/Features/Create/v1/CreateTodoCommand.cs deleted file mode 100644 index 7834dbfe81..0000000000 --- a/src/api/modules/Todo/Features/Create/v1/CreateTodoCommand.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.ComponentModel; -using MediatR; - -namespace FSH.Starter.WebApi.Todo.Features.Create.v1; -public record CreateTodoCommand( - [property: DefaultValue("Hello World!")] string Title, - [property: DefaultValue("Important Note.")] string Note) : IRequest; - - - diff --git a/src/api/modules/Todo/Features/Create/v1/CreateTodoEndpoint.cs b/src/api/modules/Todo/Features/Create/v1/CreateTodoEndpoint.cs deleted file mode 100644 index 7ad7673107..0000000000 --- a/src/api/modules/Todo/Features/Create/v1/CreateTodoEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Asp.Versioning; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Todo.Features.Create.v1; -public static class CreateTodoEndpoint -{ - internal static RouteHandlerBuilder MapTodoItemCreationEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/", async (CreateTodoCommand request, ISender mediator) => - { - var response = await mediator.Send(request); - return Results.CreatedAtRoute(nameof(CreateTodoEndpoint), new { id = response.Id }, response); - }) - .WithName(nameof(CreateTodoEndpoint)) - .WithSummary("Creates a todo item") - .WithDescription("Creates a todo item") - .Produces(StatusCodes.Status201Created) - .RequirePermission("Permissions.Todos.Create") - .MapToApiVersion(new ApiVersion(1, 0)); - - } -} diff --git a/src/api/modules/Todo/Features/Create/v1/CreateTodoHandler.cs b/src/api/modules/Todo/Features/Create/v1/CreateTodoHandler.cs deleted file mode 100644 index c910b630bd..0000000000 --- a/src/api/modules/Todo/Features/Create/v1/CreateTodoHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Features.Create.v1; -public sealed class CreateTodoHandler( - ILogger logger, - [FromKeyedServices("todo")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(CreateTodoCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var item = TodoItem.Create(request.Title, request.Note); - await repository.AddAsync(item, cancellationToken).ConfigureAwait(false); - await repository.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("todo item created {TodoItemId}", item.Id); - return new CreateTodoResponse(item.Id); - } -} diff --git a/src/api/modules/Todo/Features/Create/v1/CreateTodoResponse.cs b/src/api/modules/Todo/Features/Create/v1/CreateTodoResponse.cs deleted file mode 100644 index d50f053479..0000000000 --- a/src/api/modules/Todo/Features/Create/v1/CreateTodoResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Todo.Features.Create.v1; -public record CreateTodoResponse(Guid? Id); diff --git a/src/api/modules/Todo/Features/Create/v1/CreateTodoValidator.cs b/src/api/modules/Todo/Features/Create/v1/CreateTodoValidator.cs deleted file mode 100644 index 8549f6a3ce..0000000000 --- a/src/api/modules/Todo/Features/Create/v1/CreateTodoValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FluentValidation; -using FSH.Starter.WebApi.Todo.Persistence; - -namespace FSH.Starter.WebApi.Todo.Features.Create.v1; -public class CreateTodoValidator : AbstractValidator -{ - public CreateTodoValidator(TodoDbContext context) - { - RuleFor(p => p.Title).NotEmpty(); - RuleFor(p => p.Note).NotEmpty(); - } -} diff --git a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoCommand.cs b/src/api/modules/Todo/Features/Delete/v1/DeleteTodoCommand.cs deleted file mode 100644 index 86388518a4..0000000000 --- a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoCommand.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Todo.Features.Delete.v1; -public sealed record DeleteTodoCommand( - Guid Id) : IRequest; - - - diff --git a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoEndpoint.cs b/src/api/modules/Todo/Features/Delete/v1/DeleteTodoEndpoint.cs deleted file mode 100644 index b73b0658d9..0000000000 --- a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Asp.Versioning; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Todo.Features.Delete.v1; -public static class DeleteTodoEndpoint -{ - internal static RouteHandlerBuilder MapTodoItemDeletionEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints - .MapDelete("/{id:guid}", async (Guid id, ISender mediator) => - { - await mediator.Send(new DeleteTodoCommand(id)); - return Results.NoContent(); - }) - .WithName(nameof(DeleteTodoEndpoint)) - .WithSummary("Deletes a todo item") - .WithDescription("Deleted a todo item") - .Produces(StatusCodes.Status204NoContent) - .RequirePermission("Permissions.Todos.Delete") - .MapToApiVersion(new ApiVersion(1, 0)); - - } -} diff --git a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoHandler.cs b/src/api/modules/Todo/Features/Delete/v1/DeleteTodoHandler.cs deleted file mode 100644 index 0574538757..0000000000 --- a/src/api/modules/Todo/Features/Delete/v1/DeleteTodoHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using FSH.Starter.WebApi.Todo.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Features.Delete.v1; -public sealed class DeleteTodoHandler( - ILogger logger, - [FromKeyedServices("todo")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(DeleteTodoCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var todoItem = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = todoItem ?? throw new TodoItemNotFoundException(request.Id); - await repository.DeleteAsync(todoItem, cancellationToken); - logger.LogInformation("todo with id : {TodoId} deleted", todoItem.Id); - } -} diff --git a/src/api/modules/Todo/Features/Get/v1/GetTodoEndpoint.cs b/src/api/modules/Todo/Features/Get/v1/GetTodoEndpoint.cs deleted file mode 100644 index 1f039d9d73..0000000000 --- a/src/api/modules/Todo/Features/Get/v1/GetTodoEndpoint.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Todo.Features.Get.v1; -public static class GetTodoEndpoint -{ - internal static RouteHandlerBuilder MapGetTodoEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapGet("/{id:guid}", async (Guid id, ISender mediator) => - { - var response = await mediator.Send(new GetTodoRequest(id)); - return Results.Ok(response); - }) - .WithName(nameof(GetTodoEndpoint)) - .WithSummary("gets todo item by id") - .WithDescription("gets todo item by id") - .Produces() - .RequirePermission("Permissions.Todos.View") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Todo/Features/Get/v1/GetTodoHandler.cs b/src/api/modules/Todo/Features/Get/v1/GetTodoHandler.cs deleted file mode 100644 index 9d7d679363..0000000000 --- a/src/api/modules/Todo/Features/Get/v1/GetTodoHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FSH.Framework.Core.Caching; -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using FSH.Starter.WebApi.Todo.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.WebApi.Todo.Features.Get.v1; -public sealed class GetTodoHandler( - [FromKeyedServices("todo")] IReadRepository repository, - ICacheService cache) - : IRequestHandler -{ - public async Task Handle(GetTodoRequest request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var item = await cache.GetOrSetAsync( - $"todo:{request.Id}", - async () => - { - var todoItem = await repository.GetByIdAsync(request.Id, cancellationToken); - if (todoItem == null) throw new TodoItemNotFoundException(request.Id); - return new GetTodoResponse(todoItem.Id, todoItem.Title!, todoItem.Note!); - }, - cancellationToken: cancellationToken); - return item!; - } -} diff --git a/src/api/modules/Todo/Features/Get/v1/GetTodoRequest.cs b/src/api/modules/Todo/Features/Get/v1/GetTodoRequest.cs deleted file mode 100644 index 6569694ca5..0000000000 --- a/src/api/modules/Todo/Features/Get/v1/GetTodoRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Todo.Features.Get.v1; -public class GetTodoRequest : IRequest -{ - public Guid Id { get; set; } - public GetTodoRequest(Guid id) => Id = id; -} diff --git a/src/api/modules/Todo/Features/Get/v1/GetTodoResponse.cs b/src/api/modules/Todo/Features/Get/v1/GetTodoResponse.cs deleted file mode 100644 index 910288c95d..0000000000 --- a/src/api/modules/Todo/Features/Get/v1/GetTodoResponse.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Todo.Features.Get.v1; -public record GetTodoResponse(Guid? Id, string? Title, string? Note); diff --git a/src/api/modules/Todo/Features/GetList/v1/GetTodoListEndpoint.cs b/src/api/modules/Todo/Features/GetList/v1/GetTodoListEndpoint.cs deleted file mode 100644 index d183c3e33b..0000000000 --- a/src/api/modules/Todo/Features/GetList/v1/GetTodoListEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Todo.Features.GetList.v1; - -public static class GetTodoListEndpoint -{ - internal static RouteHandlerBuilder MapGetTodoListEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints.MapPost("/search", async (ISender mediator, [FromBody] PaginationFilter filter) => - { - var response = await mediator.Send(new GetTodoListRequest(filter)); - return Results.Ok(response); - }) - .WithName(nameof(GetTodoListEndpoint)) - .WithSummary("Gets a list of todo items with paging support") - .WithDescription("Gets a list of todo items with paging support") - .Produces>() - .RequirePermission("Permissions.Todos.View") - .MapToApiVersion(1); - } -} diff --git a/src/api/modules/Todo/Features/GetList/v1/GetTodoListHandler.cs b/src/api/modules/Todo/Features/GetList/v1/GetTodoListHandler.cs deleted file mode 100644 index d9ad5479e9..0000000000 --- a/src/api/modules/Todo/Features/GetList/v1/GetTodoListHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using FSH.Framework.Core.Paging; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Core.Specifications; -using FSH.Starter.WebApi.Todo.Domain; -using MediatR; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.WebApi.Todo.Features.GetList.v1; - -public sealed class GetTodoListHandler( - [FromKeyedServices("todo")] IReadRepository repository) - : IRequestHandler> -{ - public async Task> Handle(GetTodoListRequest request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - - var spec = new EntitiesByPaginationFilterSpec(request.Filter); - - var items = await repository.ListAsync(spec, cancellationToken).ConfigureAwait(false); - var totalCount = await repository.CountAsync(spec, cancellationToken).ConfigureAwait(false); - - return new PagedList(items, request.Filter.PageNumber, request.Filter.PageSize, totalCount); - } -} diff --git a/src/api/modules/Todo/Features/GetList/v1/GetTodoListRequest.cs b/src/api/modules/Todo/Features/GetList/v1/GetTodoListRequest.cs deleted file mode 100644 index 349fb44a88..0000000000 --- a/src/api/modules/Todo/Features/GetList/v1/GetTodoListRequest.cs +++ /dev/null @@ -1,5 +0,0 @@ -using FSH.Framework.Core.Paging; -using MediatR; - -namespace FSH.Starter.WebApi.Todo.Features.GetList.v1; -public record GetTodoListRequest(PaginationFilter Filter) : IRequest>; diff --git a/src/api/modules/Todo/Features/GetList/v1/TodoDto.cs b/src/api/modules/Todo/Features/GetList/v1/TodoDto.cs deleted file mode 100644 index 869d34eb99..0000000000 --- a/src/api/modules/Todo/Features/GetList/v1/TodoDto.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace FSH.Starter.WebApi.Todo.Features.GetList.v1; -public record TodoDto(Guid? Id, string Title, string Note); diff --git a/src/api/modules/Todo/Features/Update/v1/UpdateTodoCommand.cs b/src/api/modules/Todo/Features/Update/v1/UpdateTodoCommand.cs deleted file mode 100644 index 89b30556c1..0000000000 --- a/src/api/modules/Todo/Features/Update/v1/UpdateTodoCommand.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MediatR; - -namespace FSH.Starter.WebApi.Todo.Features.Update.v1; -public sealed record UpdateTodoCommand( - Guid Id, - string? Title, - string? Note = null): IRequest; - - - diff --git a/src/api/modules/Todo/Features/Update/v1/UpdateTodoEndpoint.cs b/src/api/modules/Todo/Features/Update/v1/UpdateTodoEndpoint.cs deleted file mode 100644 index a19b9ba901..0000000000 --- a/src/api/modules/Todo/Features/Update/v1/UpdateTodoEndpoint.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Asp.Versioning; -using FSH.Framework.Infrastructure.Auth.Policy; -using MediatR; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace FSH.Starter.WebApi.Todo.Features.Update.v1; -public static class UpdateTodoEndpoint -{ - internal static RouteHandlerBuilder MapTodoItemUpdationEndpoint(this IEndpointRouteBuilder endpoints) - { - return endpoints. - MapPut("/{id:guid}", async (Guid id, UpdateTodoCommand request, ISender mediator) => - { - if (id != request.Id) return Results.BadRequest(); - var response = await mediator.Send(request); - return Results.Ok(response); - }) - .WithName(nameof(UpdateTodoEndpoint)) - .WithSummary("Updates a todo item") - .WithDescription("Updated a todo item") - .Produces(StatusCodes.Status200OK) - .RequirePermission("Permissions.Todos.Update") - .MapToApiVersion(new ApiVersion(1, 0)); - - } -} diff --git a/src/api/modules/Todo/Features/Update/v1/UpdateTodoHandler.cs b/src/api/modules/Todo/Features/Update/v1/UpdateTodoHandler.cs deleted file mode 100644 index fa1c26653d..0000000000 --- a/src/api/modules/Todo/Features/Update/v1/UpdateTodoHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using FSH.Starter.WebApi.Todo.Exceptions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Features.Update.v1; -public sealed class UpdateTodoHandler( - ILogger logger, - [FromKeyedServices("todo")] IRepository repository) - : IRequestHandler -{ - public async Task Handle(UpdateTodoCommand request, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(request); - var todo = await repository.GetByIdAsync(request.Id, cancellationToken); - _ = todo ?? throw new TodoItemNotFoundException(request.Id); - var updatedTodo = todo.Update(request.Title, request.Note); - await repository.UpdateAsync(updatedTodo, cancellationToken); - logger.LogInformation("todo item updated {TodoItemId}", updatedTodo.Id); - return new UpdateTodoResponse(updatedTodo.Id); - } -} diff --git a/src/api/modules/Todo/Features/Update/v1/UpdateTodoResponse.cs b/src/api/modules/Todo/Features/Update/v1/UpdateTodoResponse.cs deleted file mode 100644 index b97b5c283f..0000000000 --- a/src/api/modules/Todo/Features/Update/v1/UpdateTodoResponse.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace FSH.Starter.WebApi.Todo.Features.Update.v1; -public record UpdateTodoResponse(Guid? Id); - diff --git a/src/api/modules/Todo/Features/Update/v1/UpdateTodoValidator.cs b/src/api/modules/Todo/Features/Update/v1/UpdateTodoValidator.cs deleted file mode 100644 index 9cf5935b6f..0000000000 --- a/src/api/modules/Todo/Features/Update/v1/UpdateTodoValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FluentValidation; -using FSH.Starter.WebApi.Todo.Persistence; - -namespace FSH.Starter.WebApi.Todo.Features.Update.v1; -public class UpdateTodoValidator : AbstractValidator -{ - public UpdateTodoValidator(TodoDbContext context) - { - RuleFor(p => p.Title).NotEmpty(); - RuleFor(p => p.Note).NotEmpty(); - } -} diff --git a/src/api/modules/Todo/Persistence/Configurations/TodoItemConfiguration.cs b/src/api/modules/Todo/Persistence/Configurations/TodoItemConfiguration.cs deleted file mode 100644 index 7b9a290db4..0000000000 --- a/src/api/modules/Todo/Persistence/Configurations/TodoItemConfiguration.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Finbuckle.MultiTenant; -using FSH.Starter.WebApi.Todo.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace FSH.Starter.WebApi.Todo.Persistence.Configurations; -internal sealed class TodoItemConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.IsMultiTenant(); - builder.HasKey(x => x.Id); - builder.Property(x => x.Title).HasMaxLength(100); - builder.Property(x => x.Note).HasMaxLength(1000); - } -} diff --git a/src/api/modules/Todo/Persistence/TodoDbContext.cs b/src/api/modules/Todo/Persistence/TodoDbContext.cs deleted file mode 100644 index e802801d4d..0000000000 --- a/src/api/modules/Todo/Persistence/TodoDbContext.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Finbuckle.MultiTenant.Abstractions; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Framework.Infrastructure.Tenant; -using FSH.Starter.WebApi.Todo.Domain; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Shared.Constants; - -namespace FSH.Starter.WebApi.Todo.Persistence; -public sealed class TodoDbContext : FshDbContext -{ - public TodoDbContext(IMultiTenantContextAccessor multiTenantContextAccessor, DbContextOptions options, IPublisher publisher, IOptions settings) - : base(multiTenantContextAccessor, options, publisher, settings) - { - } - - public DbSet Todos { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - ArgumentNullException.ThrowIfNull(modelBuilder); - modelBuilder.ApplyConfigurationsFromAssembly(typeof(TodoDbContext).Assembly); - modelBuilder.HasDefaultSchema(SchemaNames.Todo); - } -} diff --git a/src/api/modules/Todo/Persistence/TodoDbInitializer.cs b/src/api/modules/Todo/Persistence/TodoDbInitializer.cs deleted file mode 100644 index 77f37affb0..0000000000 --- a/src/api/modules/Todo/Persistence/TodoDbInitializer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using FSH.Framework.Core.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.WebApi.Todo.Persistence; -internal sealed class TodoDbInitializer( - ILogger logger, - TodoDbContext context) : IDbInitializer -{ - public async Task MigrateAsync(CancellationToken cancellationToken) - { - if ((await context.Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any()) - { - await context.Database.MigrateAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("[{Tenant}] applied database migrations for todo module", context.TenantInfo!.Identifier); - } - } - - public async Task SeedAsync(CancellationToken cancellationToken) - { - const string title = "Hello World!"; - const string note = "This is your first task"; - if (await context.Todos.FirstOrDefaultAsync(t => t.Title == title, cancellationToken).ConfigureAwait(false) is null) - { - var todo = TodoItem.Create(title, note); - await context.Todos.AddAsync(todo, cancellationToken); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - logger.LogInformation("[{Tenant}] seeding default todo data", context.TenantInfo!.Identifier); - } - } -} diff --git a/src/api/modules/Todo/Persistence/TodoRepository.cs b/src/api/modules/Todo/Persistence/TodoRepository.cs deleted file mode 100644 index ff3904c568..0000000000 --- a/src/api/modules/Todo/Persistence/TodoRepository.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Ardalis.Specification; -using Ardalis.Specification.EntityFrameworkCore; -using FSH.Framework.Core.Domain.Contracts; -using FSH.Framework.Core.Persistence; -using Mapster; - -namespace FSH.Starter.WebApi.Todo.Persistence; -internal sealed class TodoRepository : RepositoryBase, IReadRepository, IRepository - where T : class, IAggregateRoot -{ - public TodoRepository(TodoDbContext context) - : base(context) - { - } - - // We override the default behavior when mapping to a dto. - // We're using Mapster's ProjectToType here to immediately map the result from the database. - // This is only done when no Selector is defined, so regular specifications with a selector also still work. - protected override IQueryable ApplySpecification(ISpecification specification) => - specification.Selector is not null - ? base.ApplySpecification(specification) - : ApplySpecification(specification, false) - .ProjectToType(); -} diff --git a/src/api/modules/Todo/Todo.csproj b/src/api/modules/Todo/Todo.csproj deleted file mode 100644 index d6a5873721..0000000000 --- a/src/api/modules/Todo/Todo.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - FSH.Starter.WebApi.Todo - FSH.Starter.WebApi.Todo - - - - - - diff --git a/src/api/modules/Todo/TodoModule.cs b/src/api/modules/Todo/TodoModule.cs deleted file mode 100644 index 558d9526f0..0000000000 --- a/src/api/modules/Todo/TodoModule.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Carter; -using FSH.Framework.Core.Persistence; -using FSH.Framework.Infrastructure.Persistence; -using FSH.Starter.WebApi.Todo.Domain; -using FSH.Starter.WebApi.Todo.Features.Create.v1; -using FSH.Starter.WebApi.Todo.Features.Delete.v1; -using FSH.Starter.WebApi.Todo.Features.Get.v1; -using FSH.Starter.WebApi.Todo.Features.GetList.v1; -using FSH.Starter.WebApi.Todo.Features.Update.v1; -using FSH.Starter.WebApi.Todo.Persistence; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.WebApi.Todo; -public static class TodoModule -{ - - public class Endpoints : CarterModule - { - public override void AddRoutes(IEndpointRouteBuilder app) - { - var todoGroup = app.MapGroup("todos").WithTags("todos"); - todoGroup.MapTodoItemCreationEndpoint(); - todoGroup.MapGetTodoEndpoint(); - todoGroup.MapGetTodoListEndpoint(); - todoGroup.MapTodoItemUpdationEndpoint(); - todoGroup.MapTodoItemDeletionEndpoint(); - } - } - public static WebApplicationBuilder RegisterTodoServices(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - builder.Services.BindDbContext(); - builder.Services.AddScoped(); - builder.Services.AddKeyedScoped, TodoRepository>("todo"); - builder.Services.AddKeyedScoped, TodoRepository>("todo"); - return builder; - } - public static WebApplication UseTodoModule(this WebApplication app) - { - return app; - } -} diff --git a/src/api/server/Extensions.cs b/src/api/server/Extensions.cs deleted file mode 100644 index cc2f773e80..0000000000 --- a/src/api/server/Extensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Reflection; -using Asp.Versioning.Conventions; -using Carter; -using FluentValidation; -using FSH.Starter.WebApi.Catalog.Application; -using FSH.Starter.WebApi.Catalog.Infrastructure; -using FSH.Starter.WebApi.Todo; - -namespace FSH.Starter.WebApi.Host; - -public static class Extensions -{ - public static WebApplicationBuilder RegisterModules(this WebApplicationBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - //define module assemblies - var assemblies = new Assembly[] - { - typeof(CatalogMetadata).Assembly, - typeof(TodoModule).Assembly - }; - - //register validators - builder.Services.AddValidatorsFromAssemblies(assemblies); - - //register mediatr - builder.Services.AddMediatR(cfg => - { - cfg.RegisterServicesFromAssemblies(assemblies); - }); - - //register module services - builder.RegisterCatalogServices(); - builder.RegisterTodoServices(); - - //add carter endpoint modules - builder.Services.AddCarter(configurator: config => - { - config.WithModule(); - config.WithModule(); - }); - - return builder; - } - - public static WebApplication UseModules(this WebApplication app) - { - ArgumentNullException.ThrowIfNull(app); - - //register modules - app.UseCatalogModule(); - app.UseTodoModule(); - - //register api versions - var versions = app.NewApiVersionSet() - .HasApiVersion(1) - .HasApiVersion(2) - .ReportApiVersions() - .Build(); - - //map versioned endpoint - var endpoints = app.MapGroup("api/v{version:apiVersion}").WithApiVersionSet(versions); - - //use carter - endpoints.MapCarter(); - - return app; - } -} diff --git a/src/api/server/Program.cs b/src/api/server/Program.cs deleted file mode 100644 index f8f65e281c..0000000000 --- a/src/api/server/Program.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FSH.Framework.Infrastructure; -using FSH.Framework.Infrastructure.Logging.Serilog; -using FSH.Starter.WebApi.Host; -using Serilog; - -StaticLogger.EnsureInitialized(); -Log.Information("server booting up.."); -try -{ - var builder = WebApplication.CreateBuilder(args); - builder.ConfigureFshFramework(); - builder.RegisterModules(); - - var app = builder.Build(); - - app.UseFshFramework(); - app.UseModules(); - await app.RunAsync(); -} -catch (Exception ex) when (!ex.GetType().Name.Equals("HostAbortedException", StringComparison.Ordinal)) -{ - StaticLogger.EnsureInitialized(); - Log.Fatal(ex.Message, "unhandled exception"); -} -finally -{ - StaticLogger.EnsureInitialized(); - Log.Information("server shutting down.."); - await Log.CloseAndFlushAsync(); -} diff --git a/src/api/server/Properties/launchSettings.json b/src/api/server/Properties/launchSettings.json deleted file mode 100644 index 0ee67a65b6..0000000000 --- a/src/api/server/Properties/launchSettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "profiles": { - "Kestrel": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", - "OTEL_SERVICE_NAME": "FSH.Starter.WebApi.Host" - }, - "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7000;http://localhost:5000" - } - }, - "$schema": "http://json.schemastore.org/launchsettings.json" -} \ No newline at end of file diff --git a/src/api/server/Server.csproj b/src/api/server/Server.csproj deleted file mode 100644 index 11c255c90d..0000000000 --- a/src/api/server/Server.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - FSH.Starter.WebApi.Host - FSH.Starter.WebApi.Host - root - - - webapi - DefaultContainer - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - Always - - - Always - - - Always - - - diff --git a/src/api/server/Server.http b/src/api/server/Server.http deleted file mode 100644 index 9a38128e9d..0000000000 --- a/src/api/server/Server.http +++ /dev/null @@ -1,6 +0,0 @@ -@Server_HostAddress = http://localhost:5000 - -GET {{Server_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/src/api/server/appsettings.Development.json b/src/api/server/appsettings.Development.json deleted file mode 100644 index 351acfd5df..0000000000 --- a/src/api/server/appsettings.Development.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "DatabaseOptions": { - "Provider": "postgresql", - }, - "OriginOptions": { - "OriginUrl": "https://localhost:7000" - }, - "CacheOptions": { - "Redis": "" - }, - "HangfireOptions": { - "Username": "admin", - "Password": "Secure1234!Me", - "Route": "/jobs" - }, - "JwtOptions": { - "Key": "QsJbczCNysv/5SGh+U7sxedX8C07TPQPBdsnSDKZ/aE=", - "TokenExpirationInMinutes": 60, - "RefreshTokenExpirationInDays": 7 - }, - "MailOptions": { - "From": "mukesh@fullstackhero.net", - "Host": "smtp.ethereal.email", - "Port": 587, - "DisplayName": "Mukesh Murugan" - }, - "CorsOptions": { - "AllowedOrigins": [ - "https://localhost:7100", - "http://localhost:7100", - "http://localhost:5010" - ] - }, - "Serilog": { - "Using": [ - "Serilog.Sinks.Console" - ], - "MinimumLevel": { - "Default": "Debug" - }, - "WriteTo": [ - { - "Name": "Console" - } - ] - }, - "RateLimitOptions": { - "EnableRateLimiting": false, - "PermitLimit": 5, - "WindowInSeconds": 10, - "RejectionStatusCode": 429 - }, - "SecurityHeaderOptions": { - "Enable": true, - "Headers": { - "XContentTypeOptions": "nosniff", - "ReferrerPolicy": "no-referrer", - "XXSSProtection": "1; mode=block", - "XFrameOptions": "DENY", - "ContentSecurityPolicy": "block-all-mixed-content; style-src 'self' 'unsafe-inline'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline'", - "PermissionsPolicy": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()", - "StrictTransportSecurity": "max-age=31536000" - } - } -} \ No newline at end of file diff --git a/src/api/server/appsettings.json b/src/api/server/appsettings.json deleted file mode 100644 index 94292c5c98..0000000000 --- a/src/api/server/appsettings.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "DatabaseOptions": { - "Provider": "postgresql", - "ConnectionString": "Server=localhost;Database=fullstackhero;Port=5433;User Id=pgadmin;Password=pgadmin;" - }, - "OriginOptions": { - "OriginUrl": "https://localhost:7000" - }, - "CacheOptions": { - "Redis": "" - }, - "HangfireOptions": { - "Username": "admin", - "Password": "Secure1234!Me", - "Route": "/jobs" - }, - "JwtOptions": { - "Key": "QsJbczCNysv/5SGh+U7sxedX8C07TPQPBdsnSDKZ/aE=", - "TokenExpirationInMinutes": 10, - "RefreshTokenExpirationInDays": 7 - }, - "MailOptions": { - "From": "mukesh@fullstackhero.net", - "Host": "smtp.ethereal.email", - "Port": 587, - "UserName": "ruth.ruecker@ethereal.email", - "Password": "wygzuX6kpcK6AfDJcd", - "DisplayName": "Mukesh Murugan" - }, - "CorsOptions": { - "AllowedOrigins": [ - "https://localhost:7100", - "http://localhost:7100", - "http://localhost:5010" - ] - }, - "Serilog": { - "Using": [ - "Serilog.Sinks.Console" - ], - "MinimumLevel": { - "Default": "Debug" - }, - "WriteTo": [ - { - "Name": "Console" - } - ] - }, - "RateLimitOptions": { - "EnableRateLimiting": false, - "PermitLimit": 5, - "WindowInSeconds": 10, - "RejectionStatusCode": 429 - }, - "SecurityHeaderOptions": { - "Enable": true, - "Headers": { - "XContentTypeOptions": "nosniff", - "ReferrerPolicy": "no-referrer", - "XXSSProtection": "1; mode=block", - "XFrameOptions": "DENY", - "ContentSecurityPolicy": "block-all-mixed-content; style-src 'self' 'unsafe-inline'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline'", - "PermissionsPolicy": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()", - "StrictTransportSecurity": "max-age=31536000" - } - } -} \ No newline at end of file diff --git a/src/api/server/assets/defaults/profile-picture.webp b/src/api/server/assets/defaults/profile-picture.webp deleted file mode 100644 index d0922c3f68..0000000000 Binary files a/src/api/server/assets/defaults/profile-picture.webp and /dev/null differ diff --git a/src/apps/blazor/client/App.razor b/src/apps/blazor/client/App.razor deleted file mode 100644 index 95fc72e0f0..0000000000 --- a/src/apps/blazor/client/App.razor +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - @if (@context.User.Identity?.IsAuthenticated is true) - { -

You are not authorized to be here.

- } - else - { - - } -
-
-
- - - - - -
-
\ No newline at end of file diff --git a/src/apps/blazor/client/Client.csproj b/src/apps/blazor/client/Client.csproj deleted file mode 100644 index 9b732733b7..0000000000 --- a/src/apps/blazor/client/Client.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net9.0 - enable - enable - FSH.Starter.Blazor.Client - FSH.Starter.Blazor.Client - service-worker-assets.js - - - - - - - - - - - - - - - - - - diff --git a/src/apps/blazor/client/Components/ApiHelper.cs b/src/apps/blazor/client/Components/ApiHelper.cs deleted file mode 100644 index 8c6522a460..0000000000 --- a/src/apps/blazor/client/Components/ApiHelper.cs +++ /dev/null @@ -1,70 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Api; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components; - -public static class ApiHelper -{ - public static async Task ExecuteCallGuardedAsync( - Func> call, - ISnackbar snackbar, - NavigationManager navigationManager, - FshValidation? customValidation = null, - string? successMessage = null) - { - customValidation?.ClearErrors(); - try - { - var result = await call(); - - if (!string.IsNullOrWhiteSpace(successMessage)) - { - snackbar.Add(successMessage, Severity.Info); - } - - return result; - } - catch (ApiException ex) - { - if (ex.StatusCode == 401) - { - navigationManager.NavigateTo("/logout"); - } - var message = ex.Message switch - { - "TypeError: Failed to fetch" => "Unable to Reach API", - _ => ex.Message - }; - snackbar.Add(message, Severity.Error); - } - - return default; - } - - public static async Task ExecuteCallGuardedAsync( - Func call, - ISnackbar snackbar, - FshValidation? customValidation = null, - string? successMessage = null) - { - customValidation?.ClearErrors(); - try - { - await call(); - - if (!string.IsNullOrWhiteSpace(successMessage)) - { - snackbar.Add(successMessage, Severity.Success); - } - - return true; - } - catch (ApiException ex) - { - snackbar.Add(ex.Message, Severity.Error); - } - - return false; - } -} diff --git a/src/apps/blazor/client/Components/Common/CustomValidation.cs b/src/apps/blazor/client/Components/Common/CustomValidation.cs deleted file mode 100644 index 66ea933dad..0000000000 --- a/src/apps/blazor/client/Components/Common/CustomValidation.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Forms; - -namespace FSH.Starter.Blazor.Client.Components.Common; - -// See https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-6.0#server-validation-with-a-validator-component -public class CustomValidation : ComponentBase -{ - private ValidationMessageStore? _messageStore; - - [CascadingParameter] - private EditContext? CurrentEditContext { get; set; } - - protected override void OnInitialized() - { - if (CurrentEditContext is null) - { - throw new InvalidOperationException( - $"{nameof(CustomValidation)} requires a cascading " + - $"parameter of type {nameof(EditContext)}. " + - $"For example, you can use {nameof(CustomValidation)} " + - $"inside an {nameof(EditForm)}."); - } - - _messageStore = new(CurrentEditContext); - - CurrentEditContext.OnValidationRequested += (s, e) => - _messageStore?.Clear(); - CurrentEditContext.OnFieldChanged += (s, e) => - _messageStore?.Clear(e.FieldIdentifier); - } - - public void DisplayErrors(IDictionary> errors) - { - if (CurrentEditContext is not null && errors is not null) - { - foreach (var err in errors) - { - _messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value); - } - - CurrentEditContext.NotifyValidationStateChanged(); - } - } - - public void ClearErrors() - { - _messageStore?.Clear(); - CurrentEditContext?.NotifyValidationStateChanged(); - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/Common/DialogServiceExtensions.cs b/src/apps/blazor/client/Components/Common/DialogServiceExtensions.cs deleted file mode 100644 index 90fce2c71a..0000000000 --- a/src/apps/blazor/client/Components/Common/DialogServiceExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.Common; - -public static class DialogServiceExtensions -{ - public static Task ShowModalAsync(this IDialogService dialogService, DialogParameters parameters) - where TDialog : ComponentBase => - dialogService.ShowModal(parameters).Result!; - - public static IDialogReference ShowModal(this IDialogService dialogService, DialogParameters parameters) - where TDialog : ComponentBase - { - var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true, BackdropClick = false }; - - return dialogService.Show(string.Empty, parameters, options); - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/Common/FshCustomError.razor b/src/apps/blazor/client/Components/Common/FshCustomError.razor deleted file mode 100644 index 827318bbf1..0000000000 --- a/src/apps/blazor/client/Components/Common/FshCustomError.razor +++ /dev/null @@ -1 +0,0 @@ -Oopsie !! 😔 \ No newline at end of file diff --git a/src/apps/blazor/client/Components/Common/FshTable.cs b/src/apps/blazor/client/Components/Common/FshTable.cs deleted file mode 100644 index 3ba9fdf2a4..0000000000 --- a/src/apps/blazor/client/Components/Common/FshTable.cs +++ /dev/null @@ -1,40 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Notifications; -using FSH.Starter.Blazor.Infrastructure.Preferences; -using MediatR.Courier; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.Common; - -public class FshTable : MudTable -{ - [Inject] - private IClientPreferenceManager ClientPreferences { get; set; } = default!; - [Inject] - protected ICourier Courier { get; set; } = default!; - - - protected override async Task OnInitializedAsync() - { - if (await ClientPreferences.GetPreference() is ClientPreference clientPreference) - { - SetTablePreference(clientPreference.TablePreference); - } - - Courier.SubscribeWeak>(wrapper => - { - SetTablePreference(wrapper.Notification); - StateHasChanged(); - }); - - await base.OnInitializedAsync(); - } - - private void SetTablePreference(FshTablePreference tablePreference) - { - Dense = tablePreference.IsDense; - Striped = tablePreference.IsStriped; - Bordered = tablePreference.HasBorder; - Hover = tablePreference.IsHoverable; - } -} diff --git a/src/apps/blazor/client/Components/Common/TablePager.razor b/src/apps/blazor/client/Components/Common/TablePager.razor deleted file mode 100644 index 71bb4ed697..0000000000 --- a/src/apps/blazor/client/Components/Common/TablePager.razor +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/apps/blazor/client/Components/Dialogs/DeleteConfirmation.razor b/src/apps/blazor/client/Components/Dialogs/DeleteConfirmation.razor deleted file mode 100644 index f69c2336e6..0000000000 --- a/src/apps/blazor/client/Components/Dialogs/DeleteConfirmation.razor +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Delete Confirmation - - - - @ContentText - - - Cancel - Confirm - - - -@code { - [CascadingParameter] - IMudDialogInstance MudDialog { get; set; } = default!; - - [Parameter] - public string? ContentText { get; set; } - - void Submit() - { - MudDialog.Close(DialogResult.Ok(true)); - } - void Cancel() => MudDialog.Cancel(); -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/Dialogs/Logout.razor b/src/apps/blazor/client/Components/Dialogs/Logout.razor deleted file mode 100644 index 5a0d76f1fb..0000000000 --- a/src/apps/blazor/client/Components/Dialogs/Logout.razor +++ /dev/null @@ -1,39 +0,0 @@ -@namespace FSH.Starter.Blazor.Client.Components.Dialogs - - -@inject IAuthenticationService AuthService - - - - - - Logout Confirmation - - - - @ContentText - - - Cancel - @ButtonText - - - -@code { - [Parameter] public string? ContentText { get; set; } - - [Parameter] public string? ButtonText { get; set; } - - [Parameter] public Color Color { get; set; } - - [CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!; - - async Task Submit() - { - Navigation.NavigateTo("/logout"); - MudDialog.Close(DialogResult.Ok(true)); - } - - void Cancel() => - MudDialog.Cancel(); -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor b/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor deleted file mode 100644 index 4d18b0f3c5..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor +++ /dev/null @@ -1,49 +0,0 @@ -@typeparam TRequest - - - - - - - @if (IsCreate) - { - - } - else - { - - } - @Title - - - - - - - - - @ChildContent(RequestModel) - - - - - - - Cancel - - @if (IsCreate) - { - - Save - - } - else - { - - Update - - } - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor.cs b/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor.cs deleted file mode 100644 index dd1aa5efe2..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/AddEditModal.razor.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -public partial class AddEditModal : IAddEditModal -{ - [Parameter] - [EditorRequired] - public RenderFragment ChildContent { get; set; } = default!; - [Parameter] - [EditorRequired] - public TRequest RequestModel { get; set; } = default!; - [Parameter] - [EditorRequired] - public Func SaveFunc { get; set; } = default!; - [Parameter] - public Func? OnInitializedFunc { get; set; } - [Parameter] - [EditorRequired] - public string Title { get; set; } = default!; - [Parameter] - public bool IsCreate { get; set; } - [Parameter] - public string? SuccessMessage { get; set; } - - [CascadingParameter] - private IMudDialogInstance MudDialog { get; set; } = default!; - - private FshValidation? _customValidation; - - public void ForceRender() => StateHasChanged(); - - protected override Task OnInitializedAsync() => - OnInitializedFunc is not null - ? OnInitializedFunc() - : Task.CompletedTask; - - private async Task SaveAsync() - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => SaveFunc(RequestModel), Toast, _customValidation, SuccessMessage)) - { - MudDialog.Close(); - } - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/EntityClientTableContext.cs b/src/apps/blazor/client/Components/EntityTable/EntityClientTableContext.cs deleted file mode 100644 index b9d84fa8e7..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityClientTableContext.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -/// -/// Initialization Context for the EntityTable Component. -/// Use this one if you want to use Client Paging, Sorting and Filtering. -/// -public class EntityClientTableContext - : EntityTableContext -{ - /// - /// A function that loads all the data for the table from the api and returns a ListResult of TEntity. - /// - public Func?>> LoadDataFunc { get; } - - /// - /// A function that returns a boolean which indicates whether the supplied entity meets the search criteria - /// (the supplied string is the search string entered). - /// - public Func SearchFunc { get; } - - public EntityClientTableContext( - List> fields, - Func?>> loadDataFunc, - Func searchFunc, - Func? idFunc = null, - Func>? getDefaultsFunc = null, - Func? createFunc = null, - Func>? getDetailsFunc = null, - Func? updateFunc = null, - Func? deleteFunc = null, - string? entityName = null, - string? entityNamePlural = null, - string? entityResource = null, - string? searchAction = null, - string? createAction = null, - string? updateAction = null, - string? deleteAction = null, - string? exportAction = null, - Func? editFormInitializedFunc = null, - Func? hasExtraActionsFunc = null, - Func? canUpdateEntityFunc = null, - Func? canDeleteEntityFunc = null) - : base( - fields, - idFunc, - getDefaultsFunc, - createFunc, - getDetailsFunc, - updateFunc, - deleteFunc, - entityName, - entityNamePlural, - entityResource, - searchAction, - createAction, - updateAction, - deleteAction, - exportAction, - editFormInitializedFunc, - hasExtraActionsFunc, - canUpdateEntityFunc, - canDeleteEntityFunc) - { - LoadDataFunc = loadDataFunc; - SearchFunc = searchFunc; - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/EntityField.cs b/src/apps/blazor/client/Components/EntityTable/EntityField.cs deleted file mode 100644 index 5b9d16eaf7..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityField.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -public record EntityField(Func ValueFunc, string DisplayName, string SortLabel = "", Type? Type = null, RenderFragment? Template = null) -{ - /// - /// A function that returns the actual value of this field from the supplied entity. - /// - public Func ValueFunc { get; init; } = ValueFunc; - - /// - /// The string that's shown on the UI for this field. - /// - public string DisplayName { get; init; } = DisplayName; - - /// - /// The string that's sent to the api as property to sort on for this field. - /// This is only relevant when using server side sorting. - /// - public string SortLabel { get; init; } = SortLabel; - - /// - /// The type of the field. Default is string, but when boolean, it shows as a checkbox. - /// - public Type? Type { get; init; } = Type; - - /// - /// When supplied this template will be used for this field in stead of the default template. - /// - public RenderFragment? Template { get; init; } = Template; - - public bool CheckedForSearch { get; set; } = true; -} diff --git a/src/apps/blazor/client/Components/EntityTable/EntityServerTableContext.cs b/src/apps/blazor/client/Components/EntityTable/EntityServerTableContext.cs deleted file mode 100644 index c2fb79527a..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityServerTableContext.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Api; - -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -/// -/// Initialization Context for the EntityTable Component. -/// Use this one if you want to use Server Paging, Sorting and Filtering. -/// -public class EntityServerTableContext - : EntityTableContext -{ - /// - /// A function that loads the specified page from the api with the specified search criteria - /// and returns a PaginatedResult of TEntity. - /// - public Func>> SearchFunc { get; } - - public bool EnableAdvancedSearch { get; } - - public EntityServerTableContext( - List> fields, - Func>> searchFunc, - bool enableAdvancedSearch = false, - Func? idFunc = null, - Func>? getDefaultsFunc = null, - Func? createFunc = null, - Func>? getDetailsFunc = null, - Func? updateFunc = null, - Func? deleteFunc = null, - string? entityName = null, - string? entityNamePlural = null, - string? entityResource = null, - string? searchAction = null, - string? createAction = null, - string? updateAction = null, - string? deleteAction = null, - string? exportAction = null, - Func? editFormInitializedFunc = null, - Func? hasExtraActionsFunc = null, - Func? canUpdateEntityFunc = null, - Func? canDeleteEntityFunc = null) - : base( - fields, - idFunc, - getDefaultsFunc, - createFunc, - getDetailsFunc, - updateFunc, - deleteFunc, - entityName, - entityNamePlural, - entityResource, - searchAction, - createAction, - updateAction, - deleteAction, - exportAction, - editFormInitializedFunc, - hasExtraActionsFunc, - canUpdateEntityFunc, - canDeleteEntityFunc) - { - SearchFunc = searchFunc; - EnableAdvancedSearch = enableAdvancedSearch; - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/EntityTable.razor b/src/apps/blazor/client/Components/EntityTable/EntityTable.razor deleted file mode 100644 index cad1b9b48e..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityTable.razor +++ /dev/null @@ -1,150 +0,0 @@ -@typeparam TEntity -@typeparam TId -@typeparam TRequest - -@inject IJSRuntime JS - - - - - - - @if (_canSearch && (Context.AdvancedSearchEnabled || AdvancedSearchContent is not null)) - { - - - - @* @if (Context.AdvancedSearchEnabled) - { -
- - @foreach (var field in Context.Fields) - { - - } -
- } *@ - @AdvancedSearchContent - -
- } - - - - -
- @if (_canCreate) - { - Create - } - Reload -
- - @if (_canSearch && !_advancedSearchExpanded) - { - - - } -
- - - @if (Context.Fields is not null) - { - foreach (var field in Context.Fields) - { - - @if (Context.IsClientContext) - { - @field.DisplayName - } - else - { - @field.DisplayName - } - - } - } - Actions - - - - @foreach (var field in Context.Fields) - { - - @if (field.Template is not null) - { - @field.Template(context) - } - else if (field.Type == typeof(bool)) - { - - } - else - { - - } - - } - - @if (ActionsContent is not null) - { - @ActionsContent(context) - } - else if (HasActions) - { - - @if (CanUpdateEntity(context)) - { - Edit - } - @if (CanDeleteEntity(context)) - { - Delete - } - @if (ExtraActions is not null) - { - @ExtraActions(context) - } - - } - else - { - - No Allowed Actions - - } - - - - - - - -
- -
- - - -
\ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/EntityTable.razor.cs b/src/apps/blazor/client/Components/EntityTable/EntityTable.razor.cs deleted file mode 100644 index fe7bd405cd..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityTable.razor.cs +++ /dev/null @@ -1,287 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.Common; -using FSH.Starter.Blazor.Client.Components.Dialogs; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using Mapster; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -public partial class EntityTable - where TRequest : new() -{ - [Parameter] - [EditorRequired] - public EntityTableContext Context { get; set; } = default!; - - [Parameter] - public bool Loading { get; set; } - - [Parameter] - public string? SearchString { get; set; } - [Parameter] - public EventCallback SearchStringChanged { get; set; } - - [Parameter] - public RenderFragment? AdvancedSearchContent { get; set; } - - [Parameter] - public RenderFragment? ActionsContent { get; set; } - [Parameter] - public RenderFragment? ExtraActions { get; set; } - [Parameter] - public RenderFragment? ChildRowContent { get; set; } - - [Parameter] - public RenderFragment? EditFormContent { get; set; } - - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - - private bool _canSearch; - private bool _canCreate; - private bool _canUpdate; - private bool _canDelete; - private bool _canExport; - - private bool _advancedSearchExpanded; - - private MudTable _table = default!; - private IEnumerable? _entityList; - private int _totalItems; - - protected override async Task OnInitializedAsync() - { - var state = await AuthState; - _canSearch = await CanDoActionAsync(Context.SearchAction, state); - _canCreate = await CanDoActionAsync(Context.CreateAction, state); - _canUpdate = await CanDoActionAsync(Context.UpdateAction, state); - _canDelete = await CanDoActionAsync(Context.DeleteAction, state); - _canExport = await CanDoActionAsync(Context.ExportAction, state); - - await LocalLoadDataAsync(); - } - - public Task ReloadDataAsync() => - Context.IsClientContext - ? LocalLoadDataAsync() - : ServerLoadDataAsync(); - - private async Task CanDoActionAsync(string? action, AuthenticationState state) => - !string.IsNullOrWhiteSpace(action) && - (bool.TryParse(action, out bool isTrue) && isTrue || // check if action equals "True", then it's allowed - Context.EntityResource is { } resource && await AuthService.HasPermissionAsync(state.User, action, resource)); - - private bool HasActions => _canUpdate || _canDelete || Context.HasExtraActionsFunc is not null && Context.HasExtraActionsFunc(); - private bool CanUpdateEntity(TEntity entity) => _canUpdate && (Context.CanUpdateEntityFunc is null || Context.CanUpdateEntityFunc(entity)); - private bool CanDeleteEntity(TEntity entity) => _canDelete && (Context.CanDeleteEntityFunc is null || Context.CanDeleteEntityFunc(entity)); - - // Client side paging/filtering - private bool LocalSearch(TEntity entity) => - Context.ClientContext?.SearchFunc is { } searchFunc - ? searchFunc(SearchString, entity) - : string.IsNullOrWhiteSpace(SearchString); - - private async Task LocalLoadDataAsync() - { - if (Loading || Context.ClientContext is null) - { - return; - } - - Loading = true; - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => Context.ClientContext.LoadDataFunc(), Toast, Navigation) - is List result) - { - _entityList = result; - } - - Loading = false; - } - - // Server Side paging/filtering - - private async Task OnSearchStringChanged(string? text = null) - { - await SearchStringChanged.InvokeAsync(SearchString); - - await ServerLoadDataAsync(); - } - - private async Task ServerLoadDataAsync() - { - if (Context.IsServerContext) - { - await _table.ReloadServerData(); - } - } - - private static bool GetBooleanValue(object valueFunc) - { - if (valueFunc is bool boolValue) - { - return boolValue; - } - return false; - } - - private Func>>? ServerReloadFunc => - Context.IsServerContext ? ServerReload : null; - - private async Task> ServerReload(TableState state, CancellationToken cancellationToken) - { - if (!Loading && Context.ServerContext is not null) - { - Loading = true; - - var filter = GetPaginationFilter(state); - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => Context.ServerContext.SearchFunc(filter), Toast, Navigation) - is { } result) - { - _totalItems = result.TotalCount; - _entityList = result.Items; - } - - Loading = false; - } - - return new TableData { TotalItems = _totalItems, Items = _entityList }; - } - - - private PaginationFilter GetPaginationFilter(TableState state) - { - string[]? orderings = null; - if (!string.IsNullOrEmpty(state.SortLabel)) - { - orderings = state.SortDirection == SortDirection.None - ? new[] { $"{state.SortLabel}" } - : new[] { $"{state.SortLabel} {state.SortDirection}" }; - } - - var filter = new PaginationFilter - { - PageSize = state.PageSize, - PageNumber = state.Page + 1, - Keyword = SearchString, - OrderBy = orderings ?? Array.Empty() - }; - - if (!Context.AllColumnsChecked) - { - filter.AdvancedSearch = new() - { - Fields = Context.SearchFields, - Keyword = filter.Keyword - }; - filter.Keyword = null; - } - - return filter; - } - - private async Task InvokeModal(TEntity? entity = default) - { - bool isCreate = entity is null; - - var parameters = new DialogParameters() - { - { nameof(AddEditModal.ChildContent), EditFormContent }, - { nameof(AddEditModal.OnInitializedFunc), Context.EditFormInitializedFunc }, - { nameof(AddEditModal.IsCreate), isCreate } - }; - - Func saveFunc; - TRequest requestModel; - string title, successMessage; - - if (isCreate) - { - _ = Context.CreateFunc ?? throw new InvalidOperationException("CreateFunc can't be null!"); - - saveFunc = Context.CreateFunc; - - requestModel = - Context.GetDefaultsFunc is not null - && await ApiHelper.ExecuteCallGuardedAsync( - () => Context.GetDefaultsFunc(), Toast, Navigation) - is { } defaultsResult - ? defaultsResult - : new TRequest(); - - title = $"Create {Context.EntityName}"; - successMessage = $"{Context.EntityName} Created"; - } - else - { - _ = Context.IdFunc ?? throw new InvalidOperationException("IdFunc can't be null!"); - _ = Context.UpdateFunc ?? throw new InvalidOperationException("UpdateFunc can't be null!"); - - var id = Context.IdFunc(entity!); - - saveFunc = request => Context.UpdateFunc(id, request); - - requestModel = - Context.GetDetailsFunc is not null - && await ApiHelper.ExecuteCallGuardedAsync( - () => Context.GetDetailsFunc(id!), - Toast, Navigation) - is { } detailsResult - ? detailsResult - : entity!.Adapt(); - - title = $"Edit {Context.EntityName}"; - successMessage = $"{Context.EntityName}Updated"; - } - - parameters.Add(nameof(AddEditModal.SaveFunc), saveFunc); - parameters.Add(nameof(AddEditModal.RequestModel), requestModel); - parameters.Add(nameof(AddEditModal.Title), title); - parameters.Add(nameof(AddEditModal.SuccessMessage), successMessage); - - var dialog = DialogService.ShowModal>(parameters); - - Context.SetAddEditModalRef(dialog); - - var result = await dialog.Result; - - if (!result!.Canceled) - { - await ReloadDataAsync(); - } - } - - private async Task Delete(TEntity entity) - { - _ = Context.IdFunc ?? throw new InvalidOperationException("IdFunc can't be null!"); - TId id = Context.IdFunc(entity); - - string deleteContent = "You're sure you want to delete {0} with id '{1}'?"; - var parameters = new DialogParameters - { - { nameof(DeleteConfirmation.ContentText), string.Format(deleteContent, Context.EntityName, id) } - }; - var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true, BackdropClick = false }; - var dialog = DialogService.Show("Delete", parameters, options); - var result = await dialog.Result; - if (!result!.Canceled) - { - _ = Context.DeleteFunc ?? throw new InvalidOperationException("DeleteFunc can't be null!"); - - await ApiHelper.ExecuteCallGuardedAsync( - () => Context.DeleteFunc(id), - Toast); - - await ReloadDataAsync(); - } - } -} diff --git a/src/apps/blazor/client/Components/EntityTable/EntityTableContext.cs b/src/apps/blazor/client/Components/EntityTable/EntityTableContext.cs deleted file mode 100644 index 336e256ef9..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/EntityTableContext.cs +++ /dev/null @@ -1,197 +0,0 @@ -using FSH.Starter.Shared.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -/// -/// Abstract base class for the initialization Context of the EntityTable Component. -/// -/// The type of the entity. -/// The type of the id of the entity. -/// The type of the Request which is used on the AddEditModal and which is sent with the CreateFunc and UpdateFunc. -public abstract class EntityTableContext -{ - /// - /// The columns you want to display on the table. - /// - public List> Fields { get; } - - /// - /// A function that returns the Id of the entity. This is only needed when using the CRUD functionality. - /// - public Func? IdFunc { get; } - - /// - /// A function that executes the GetDefaults method on the api (or supplies defaults locally) and returns - /// a Task of Result of TRequest. When not supplied, a TRequest is simply newed up. - /// No need to check for error messages or api exceptions. These are automatically handled by the component. - /// - public Func>? GetDefaultsFunc { get; } - - /// - /// A function that executes the Create method on the api with the supplied entity and returns a Task of Result. - /// No need to check for error messages or api exceptions. These are automatically handled by the component. - /// - public Func? CreateFunc { get; } - - /// - /// A function that executes the GetDetails method on the api with the supplied Id and returns a Task of Result of TRequest. - /// No need to check for error messages or api exceptions. These are automatically handled by the component. - /// When not supplied, the TEntity out of the _entityList is supplied using the IdFunc and converted using mapster. - /// - public Func>? GetDetailsFunc { get; } - - /// - /// A function that executes the Update method on the api with the supplied entity and returns a Task of Result. - /// When not supplied, the TEntity from the list is mapped to TCreateRequest using mapster. - /// No need to check for error messages or api exceptions. These are automatically handled by the component. - /// - public Func? UpdateFunc { get; } - - /// - /// A function that executes the Delete method on the api with the supplied entity id and returns a Task of Result. - /// No need to check for error messages or api exceptions. These are automatically handled by the component. - /// - public Func? DeleteFunc { get; } - - /// - /// The name of the entity. This is used in the title of the add/edit modal and delete confirmation. - /// - public string? EntityName { get; } - - /// - /// The plural name of the entity. This is used in the "Search for ..." placeholder. - /// - public string? EntityNamePlural { get; } - - /// - /// The FSHResource that is representing this entity. This is used in combination with the xxActions to check for permissions. - /// - public string? EntityResource { get; } - - /// - /// The FSHAction name of the search permission. This is FSHAction.Search by default. - /// When empty, no search functionality will be available. - /// When the string is "true", search funtionality will be enabled, - /// otherwise it will only be enabled if the user has permission for this action on the EntityResource. - /// - public string SearchAction { get; } - - /// - /// The permission name of the create permission. This is FSHAction.Create by default. - /// When empty, no create functionality will be available. - /// When the string "true", create funtionality will be enabled, - /// otherwise it will only be enabled if the user has permission for this action on the EntityResource. - /// - public string CreateAction { get; } - - /// - /// The permission name of the update permission. This is FSHAction.Update by default. - /// When empty, no update functionality will be available. - /// When the string is "true", update funtionality will be enabled, - /// otherwise it will only be enabled if the user has permission for this action on the EntityResource. - /// - public string UpdateAction { get; } - - /// - /// The permission name of the delete permission. This is FSHAction.Delete by default. - /// When empty, no delete functionality will be available. - /// When the string is "true", delete funtionality will be enabled, - /// otherwise it will only be enabled if the user has permission for this action on the EntityResource. - /// - public string DeleteAction { get; } - - /// - /// The permission name of the export permission. This is FSHAction.Export by default. - /// - public string ExportAction { get; } - - /// - /// Use this if you want to run initialization during OnInitialized of the AddEdit form. - /// - public Func? EditFormInitializedFunc { get; } - - /// - /// Use this if you want to check for permissions of content in the ExtraActions RenderFragment. - /// The extra actions won't be available when this returns false. - /// - public Func? HasExtraActionsFunc { get; set; } - - /// - /// Use this if you want to disable the update functionality for specific entities in the table. - /// - public Func? CanUpdateEntityFunc { get; set; } - - /// - /// Use this if you want to disable the delete functionality for specific entities in the table. - /// - public Func? CanDeleteEntityFunc { get; set; } - - public EntityTableContext( - List> fields, - Func? idFunc, - Func>? getDefaultsFunc, - Func? createFunc, - Func>? getDetailsFunc, - Func? updateFunc, - Func? deleteFunc, - string? entityName, - string? entityNamePlural, - string? entityResource, - string? searchAction, - string? createAction, - string? updateAction, - string? deleteAction, - string? exportAction, - Func? editFormInitializedFunc, - Func? hasExtraActionsFunc, - Func? canUpdateEntityFunc, - Func? canDeleteEntityFunc) - { - EntityResource = entityResource; - Fields = fields; - EntityName = entityName; - EntityNamePlural = entityNamePlural; - IdFunc = idFunc; - GetDefaultsFunc = getDefaultsFunc; - CreateFunc = createFunc; - GetDetailsFunc = getDetailsFunc; - UpdateFunc = updateFunc; - DeleteFunc = deleteFunc; - SearchAction = searchAction ?? FshActions.Search; - CreateAction = createAction ?? FshActions.Create; - UpdateAction = updateAction ?? FshActions.Update; - DeleteAction = deleteAction ?? FshActions.Delete; - ExportAction = exportAction ?? FshActions.Export; - EditFormInitializedFunc = editFormInitializedFunc; - HasExtraActionsFunc = hasExtraActionsFunc; - CanUpdateEntityFunc = canUpdateEntityFunc; - CanDeleteEntityFunc = canDeleteEntityFunc; - } - - // AddEdit modal - private IDialogReference? _addEditModalRef; - - internal void SetAddEditModalRef(IDialogReference dialog) => - _addEditModalRef = dialog; - - public IAddEditModal AddEditModal => - _addEditModalRef?.Dialog as IAddEditModal - ?? throw new InvalidOperationException("AddEditModal is only available when the modal is shown."); - - // Shortcuts - public EntityClientTableContext? ClientContext => this as EntityClientTableContext; - public EntityServerTableContext? ServerContext => this as EntityServerTableContext; - public bool IsClientContext => ClientContext is not null; - public bool IsServerContext => ServerContext is not null; - - // Advanced Search - public bool AllColumnsChecked => - Fields.All(f => f.CheckedForSearch); - public void AllColumnsCheckChanged(bool checkAll) => - Fields.ForEach(f => f.CheckedForSearch = checkAll); - public bool AdvancedSearchEnabled => - ServerContext?.EnableAdvancedSearch is true; - public List SearchFields => - Fields.Where(f => f.CheckedForSearch).Select(f => f.SortLabel).ToList(); -} diff --git a/src/apps/blazor/client/Components/EntityTable/IAddEditModal.cs b/src/apps/blazor/client/Components/EntityTable/IAddEditModal.cs deleted file mode 100644 index 3ba1ea09bd..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/IAddEditModal.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -public interface IAddEditModal -{ - TRequest RequestModel { get; } - bool IsCreate { get; } - void ForceRender(); -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/EntityTable/PaginationResponse.cs b/src/apps/blazor/client/Components/EntityTable/PaginationResponse.cs deleted file mode 100644 index 07451aced2..0000000000 --- a/src/apps/blazor/client/Components/EntityTable/PaginationResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSH.Starter.Blazor.Client.Components.EntityTable; - -public class PaginationResponse -{ - public List Items { get; set; } = default!; - public int TotalCount { get; set; } - public int CurrentPage { get; set; } = 1; - public int PageSize { get; set; } = 10; -} diff --git a/src/apps/blazor/client/Components/FshValidation.cs b/src/apps/blazor/client/Components/FshValidation.cs deleted file mode 100644 index 1f3ba24ebb..0000000000 --- a/src/apps/blazor/client/Components/FshValidation.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Forms; - -namespace FSH.Starter.Blazor.Client.Components; - -// See https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-6.0#server-validation-with-a-validator-component -public class FshValidation : ComponentBase -{ - private ValidationMessageStore? _messageStore; - - [CascadingParameter] - private EditContext? CurrentEditContext { get; set; } - - protected override void OnInitialized() - { - if (CurrentEditContext is null) - { - throw new InvalidOperationException( - $"{nameof(FshValidation)} requires a cascading " + - $"parameter of type {nameof(EditContext)}. " + - $"For example, you can use {nameof(FshValidation)} " + - $"inside an {nameof(EditForm)}."); - } - - _messageStore = new(CurrentEditContext); - - CurrentEditContext.OnValidationRequested += (s, e) => - _messageStore?.Clear(); - CurrentEditContext.OnFieldChanged += (s, e) => - _messageStore?.Clear(e.FieldIdentifier); - } - - public void DisplayErrors(IDictionary> errors) - { - if (CurrentEditContext is not null && errors is not null) - { - foreach (var err in errors) - { - _messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value); - } - - CurrentEditContext.NotifyValidationStateChanged(); - } - } - - public void ClearErrors() - { - _messageStore?.Clear(); - CurrentEditContext?.NotifyValidationStateChanged(); - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Components/General/PageHeader.razor b/src/apps/blazor/client/Components/General/PageHeader.razor deleted file mode 100644 index 07fa0453bb..0000000000 --- a/src/apps/blazor/client/Components/General/PageHeader.razor +++ /dev/null @@ -1,16 +0,0 @@ -@using MudBlazor -
- @Title - @Header - @SubHeader -
-@code { - [Parameter] - public required string Title { get; set; } - - [Parameter] - public required string Header { get; set; } - - [Parameter] - public required string SubHeader { get; set; } -} diff --git a/src/apps/blazor/client/Components/PersonCard.razor b/src/apps/blazor/client/Components/PersonCard.razor deleted file mode 100644 index 15ea230760..0000000000 --- a/src/apps/blazor/client/Components/PersonCard.razor +++ /dev/null @@ -1,21 +0,0 @@ - - - - @if (string.IsNullOrEmpty(this.ImageUri)) - { - @FullName?.ToUpper().FirstOrDefault() - - } - else - { - - - - } - - - @FullName - @Email - - - diff --git a/src/apps/blazor/client/Components/PersonCard.razor.cs b/src/apps/blazor/client/Components/PersonCard.razor.cs deleted file mode 100644 index 23762f0e93..0000000000 --- a/src/apps/blazor/client/Components/PersonCard.razor.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -namespace FSH.Starter.Blazor.Client.Components; - -public partial class PersonCard -{ - [Parameter] - public string? Class { get; set; } - [Parameter] - public string? Style { get; set; } - - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - - private string? UserId { get; set; } - private string? Email { get; set; } - private string? FullName { get; set; } - private string? ImageUri { get; set; } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await LoadUserData(); - } - } - - private async Task LoadUserData() - { - var user = (await AuthState).User; - if (user.Identity?.IsAuthenticated == true && string.IsNullOrEmpty(UserId)) - { - FullName = user.GetFullName(); - UserId = user.GetUserId(); - Email = user.GetEmail(); - if (user.GetImageUrl() != null) - { - ImageUri = user.GetImageUrl()!.ToString(); - } - StateHasChanged(); - } - } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor b/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor deleted file mode 100644 index 2b90017e72..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor +++ /dev/null @@ -1,27 +0,0 @@ - - -
- @ColorType - - -
-
- - - - - @foreach (var color in Colors) - { - - -
-
-
-
- } -
-
-
-
-
\ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor.cs b/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor.cs deleted file mode 100644 index 3f5bd8545f..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ColorPanel.razor.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class ColorPanel -{ - [Parameter] - public List Colors { get; set; } = new(); - - [Parameter] - public string ColorType { get; set; } = string.Empty; - - [Parameter] - public Color CurrentColor { get; set; } - - [Parameter] - public EventCallback OnColorClicked { get; set; } - - protected async Task ColorClicked(string color) - { - await OnColorClicked.InvokeAsync(color); - } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor b/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor deleted file mode 100644 index c7a4e1e8db..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor +++ /dev/null @@ -1,17 +0,0 @@ - - -
- @if (_isDarkMode) - { - Switch to Light Mode - } - else - { - Switch to Dark Mode - } - -
-
-
\ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor.cs b/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor.cs deleted file mode 100644 index 1a07a9be7b..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/DarkModePanel.razor.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Preferences; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class DarkModePanel -{ - private bool _isDarkMode; - - protected override async Task OnInitializedAsync() - { - if (await ClientPreferences.GetPreference() is not ClientPreference themePreference) themePreference = new ClientPreference(); - _isDarkMode = themePreference.IsDarkMode; - } - - [Parameter] - public EventCallback OnIconClicked { get; set; } - - private async Task ToggleDarkMode() - { - _isDarkMode = !_isDarkMode; - await OnIconClicked.InvokeAsync(_isDarkMode); - } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor b/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor deleted file mode 100644 index 56e42f21d8..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor +++ /dev/null @@ -1,15 +0,0 @@ - - -
- Border Radius - @Radius.ToString() - -
-
- - - @Radius.ToString() - -
\ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor.cs b/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor.cs deleted file mode 100644 index 1c5fc62ab5..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/RadiusPanel.razor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Preferences; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class RadiusPanel -{ - [Parameter] - public double Radius { get; set; } - - [Parameter] - public double MaxValue { get; set; } = 30; - - [Parameter] - public EventCallback OnSliderChanged { get; set; } - - protected override async Task OnInitializedAsync() - { - if (await ClientPreferences.GetPreference() is not ClientPreference themePreference) themePreference = new ClientPreference(); - Radius = themePreference.BorderRadius; - } - - private async Task ChangedSelection(ChangeEventArgs args) - { - Radius = int.Parse(args?.Value?.ToString() ?? "0"); - await OnSliderChanged.InvokeAsync(Radius); - } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor b/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor deleted file mode 100644 index e748aea63e..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor +++ /dev/null @@ -1,19 +0,0 @@ - - -
- Table Customization - T - -
-
- - - - - - -
\ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor.cs b/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor.cs deleted file mode 100644 index 3bc43580fd..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/TableCustomizationPanel.razor.cs +++ /dev/null @@ -1,74 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Notifications; -using FSH.Starter.Blazor.Infrastructure.Preferences; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class TableCustomizationPanel -{ - [Parameter] - public bool IsDense { get; set; } - [Parameter] - public bool IsStriped { get; set; } - [Parameter] - public bool HasBorder { get; set; } - [Parameter] - public bool IsHoverable { get; set; } - [Inject] - protected INotificationPublisher Notifications { get; set; } = default!; - - private FshTablePreference _tablePreference = new(); - - protected override async Task OnInitializedAsync() - { - if (await ClientPreferences.GetPreference() is ClientPreference clientPreference) - { - _tablePreference = clientPreference.TablePreference; - } - - IsDense = _tablePreference.IsDense; - IsStriped = _tablePreference.IsStriped; - HasBorder = _tablePreference.HasBorder; - IsHoverable = _tablePreference.IsHoverable; - } - - [Parameter] - public EventCallback OnDenseSwitchToggled { get; set; } - - [Parameter] - public EventCallback OnStripedSwitchToggled { get; set; } - - [Parameter] - public EventCallback OnBorderdedSwitchToggled { get; set; } - - [Parameter] - public EventCallback OnHoverableSwitchToggled { get; set; } - - private async Task ToggleDenseSwitch() - { - _tablePreference.IsDense = !_tablePreference.IsDense; - await OnDenseSwitchToggled.InvokeAsync(_tablePreference.IsDense); - await Notifications.PublishAsync(_tablePreference); - } - - private async Task ToggleStripedSwitch() - { - _tablePreference.IsStriped = !_tablePreference.IsStriped; - await OnStripedSwitchToggled.InvokeAsync(_tablePreference.IsStriped); - await Notifications.PublishAsync(_tablePreference); - } - - private async Task ToggleBorderedSwitch() - { - _tablePreference.HasBorder = !_tablePreference.HasBorder; - await OnBorderdedSwitchToggled.InvokeAsync(_tablePreference.HasBorder); - await Notifications.PublishAsync(_tablePreference); - } - - private async Task ToggleHoverableSwitch() - { - _tablePreference.IsHoverable = !_tablePreference.IsHoverable; - await OnHoverableSwitchToggled.InvokeAsync(_tablePreference.IsHoverable); - await Notifications.PublishAsync(_tablePreference); - } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor b/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor deleted file mode 100644 index 364ecc5773..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor +++ /dev/null @@ -1,16 +0,0 @@ -
- - - -
- - \ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor.cs b/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor.cs deleted file mode 100644 index d5eb45af40..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ThemeButton.razor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Web; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class ThemeButton -{ - [Parameter] - public EventCallback OnClick { get; set; } -} diff --git a/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor b/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor deleted file mode 100644 index ee4022c274..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor +++ /dev/null @@ -1,28 +0,0 @@ - - - - Theme Manager - - - - - -
- - - - - - - -
-
- \ No newline at end of file diff --git a/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor.cs b/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor.cs deleted file mode 100644 index a7373b86f8..0000000000 --- a/src/apps/blazor/client/Components/ThemeManager/ThemeDrawer.razor.cs +++ /dev/null @@ -1,96 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Preferences; -using FSH.Starter.Blazor.Infrastructure.Themes; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Components.ThemeManager; - -public partial class ThemeDrawer -{ - [Parameter] - public bool ThemeDrawerOpen { get; set; } - - [Parameter] - public EventCallback ThemeDrawerOpenChanged { get; set; } - - [EditorRequired] - [Parameter] - public ClientPreference ThemePreference { get; set; } = default!; - - [EditorRequired] - [Parameter] - public EventCallback ThemePreferenceChanged { get; set; } - - private readonly List _colors = CustomColors.ThemeColors; - - private async Task UpdateThemePrimaryColor(string color) - { - if (ThemePreference is not null) - { - ThemePreference.PrimaryColor = color; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task UpdateThemeSecondaryColor(string color) - { - if (ThemePreference is not null) - { - ThemePreference.SecondaryColor = color; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task UpdateBorderRadius(double radius) - { - if (ThemePreference is not null) - { - ThemePreference.BorderRadius = radius; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task ToggleDarkLightMode(bool isDarkMode) - { - if (ThemePreference is not null) - { - ThemePreference.IsDarkMode = isDarkMode; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task ToggleEntityTableDense(bool isDense) - { - if (ThemePreference is not null) - { - ThemePreference.TablePreference.IsDense = isDense; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task ToggleEntityTableStriped(bool isStriped) - { - if (ThemePreference is not null) - { - ThemePreference.TablePreference.IsStriped = isStriped; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task ToggleEntityTableBorder(bool hasBorder) - { - if (ThemePreference is not null) - { - ThemePreference.TablePreference.HasBorder = hasBorder; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } - - private async Task ToggleEntityTableHoverable(bool isHoverable) - { - if (ThemePreference is not null) - { - ThemePreference.TablePreference.IsHoverable = isHoverable; - await ThemePreferenceChanged.InvokeAsync(ThemePreference); - } - } -} diff --git a/src/apps/blazor/client/Directory.Packages.props b/src/apps/blazor/client/Directory.Packages.props deleted file mode 100644 index 5a7acff5d7..0000000000 --- a/src/apps/blazor/client/Directory.Packages.props +++ /dev/null @@ -1,22 +0,0 @@ - - - true - true - true - - - true - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Layout/BaseLayout.razor b/src/apps/blazor/client/Layout/BaseLayout.razor deleted file mode 100644 index bd368f82ff..0000000000 --- a/src/apps/blazor/client/Layout/BaseLayout.razor +++ /dev/null @@ -1,29 +0,0 @@ -@inherits LayoutComponentBase - - - - - - - - - - - - @Body - - - - - - - - - - @Body - - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Layout/BaseLayout.razor.cs b/src/apps/blazor/client/Layout/BaseLayout.razor.cs deleted file mode 100644 index 524a2923df..0000000000 --- a/src/apps/blazor/client/Layout/BaseLayout.razor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Preferences; -using FSH.Starter.Blazor.Infrastructure.Themes; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Layout; - -public partial class BaseLayout -{ - private ClientPreference? _themePreference; - private MudTheme _currentTheme = new FshTheme(); - private bool _themeDrawerOpen; - private bool _rightToLeft; - private bool _isDarkMode; - - protected override async Task OnInitializedAsync() - { - _themePreference = await ClientPreferences.GetPreference() as ClientPreference; - if (_themePreference == null) _themePreference = new ClientPreference(); - SetCurrentTheme(_themePreference); - - Toast.Add("Like this project? ", Severity.Info, config => - { - config.BackgroundBlurred = true; - config.Icon = Icons.Custom.Brands.GitHub; - config.Action = "Star us on Github!"; - config.ActionColor = Color.Info; - config.OnClick = snackbar => - { - Navigation.NavigateTo("https://github.com/fullstackhero/dotnet-starter-kit"); - return Task.CompletedTask; - }; - }); - } - - private async Task ToggleDarkLightMode(bool isDarkMode) - { - if (_themePreference is not null) - { - _themePreference.IsDarkMode = isDarkMode; - await ThemePreferenceChanged(_themePreference); - } - } - - private async Task ThemePreferenceChanged(ClientPreference themePreference) - { - SetCurrentTheme(themePreference); - await ClientPreferences.SetPreference(themePreference); - } - - private void SetCurrentTheme(ClientPreference themePreference) - { - _isDarkMode = themePreference.IsDarkMode; - _currentTheme.PaletteLight.Primary = themePreference.PrimaryColor; - _currentTheme.PaletteLight.Secondary = themePreference.SecondaryColor; - _currentTheme.PaletteDark.Primary = themePreference.PrimaryColor; - _currentTheme.PaletteDark.Secondary = themePreference.SecondaryColor; - _currentTheme.LayoutProperties.DefaultBorderRadius = $"{themePreference.BorderRadius}px"; - _currentTheme.LayoutProperties.DefaultBorderRadius = $"{themePreference.BorderRadius}px"; - _rightToLeft = themePreference.IsRTL; - } -} diff --git a/src/apps/blazor/client/Layout/MainLayout.razor b/src/apps/blazor/client/Layout/MainLayout.razor deleted file mode 100644 index 520832e8cb..0000000000 --- a/src/apps/blazor/client/Layout/MainLayout.razor +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - fullstackhero - - - - - Sponsor - - - - Community - Discord - Facebook - - LinkedIn - Buy Me a Coffee! - - Open Collective - - Resources - Documentation - - - - - - - - - - - - -
- - -
- Community - Discord - Facebook - - HrefedIn - Resources - - MudBlazor Documentation - - Quick-Start Guide -
-
- - - - - -
- -
- - Account -
-
- -
- - Dashboard -
-
-
- - Logout - -
-
-
-
-
- - - - - - - @ChildContent - - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Layout/MainLayout.razor.cs b/src/apps/blazor/client/Layout/MainLayout.razor.cs deleted file mode 100644 index 2b24a45c4e..0000000000 --- a/src/apps/blazor/client/Layout/MainLayout.razor.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Preferences; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Layout; - -public partial class MainLayout -{ - [Parameter] - public RenderFragment ChildContent { get; set; } = default!; - [Parameter] - public EventCallback OnDarkModeToggle { get; set; } - [Parameter] - public EventCallback OnRightToLeftToggle { get; set; } - - private bool _drawerOpen; - private bool _isDarkMode; - - protected override async Task OnInitializedAsync() - { - if (await ClientPreferences.GetPreference() is ClientPreference preferences) - { - _drawerOpen = preferences.IsDrawerOpen; - _isDarkMode = preferences.IsDarkMode; - } - } - - public async Task ToggleDarkMode() - { - _isDarkMode = !_isDarkMode; - await OnDarkModeToggle.InvokeAsync(_isDarkMode); - } - - private async Task DrawerToggle() - { - _drawerOpen = await ClientPreferences.ToggleDrawerAsync(); - } - private void Logout() - { - var parameters = new DialogParameters - { - { nameof(Components.Dialogs.Logout.ContentText), "Do you want to logout from the system?"}, - { nameof(Components.Dialogs.Logout.ButtonText), "Logout"}, - { nameof(Components.Dialogs.Logout.Color), Color.Error} - }; - - var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true }; - DialogService.Show("Logout", parameters, options); - } - - private void Profile() - { - Navigation.NavigateTo("/identity/account"); - } -} diff --git a/src/apps/blazor/client/Layout/MainLayout.razor.css b/src/apps/blazor/client/Layout/MainLayout.razor.css deleted file mode 100644 index 1a95cc0551..0000000000 --- a/src/apps/blazor/client/Layout/MainLayout.razor.css +++ /dev/null @@ -1,81 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } - - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } - - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row { - justify-content: space-between; - } - - .top-row ::deep a, .top-row ::deep .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .top-row.auth ::deep a:first-child { - flex: 1; - text-align: right; - width: 0; - } - - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} - -.fsh-shadow { - box-shadow: 0 30px 60px rgba(0,0,0,0.12) !important; -} \ No newline at end of file diff --git a/src/apps/blazor/client/Layout/NavMenu.razor b/src/apps/blazor/client/Layout/NavMenu.razor deleted file mode 100644 index 86ab42d0d2..0000000000 --- a/src/apps/blazor/client/Layout/NavMenu.razor +++ /dev/null @@ -1,32 +0,0 @@ - - - Start - Home - Counter - @if (_canViewAuditTrails) - { - Audit Trail - } - Modules - - Products - Brands - - Todos - @if (CanViewAdministrationGroup) - { - Administration - @if (_canViewUsers) - { - Users - } - @if (_canViewRoles) - { - Roles - } - @if (_canViewTenants) - { - Tenants - } - } - diff --git a/src/apps/blazor/client/Layout/NavMenu.razor.cs b/src/apps/blazor/client/Layout/NavMenu.razor.cs deleted file mode 100644 index 41b598a485..0000000000 --- a/src/apps/blazor/client/Layout/NavMenu.razor.cs +++ /dev/null @@ -1,40 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; - -namespace FSH.Starter.Blazor.Client.Layout; - -public partial class NavMenu -{ - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - - private bool _canViewHangfire; - private bool _canViewDashboard; - private bool _canViewRoles; - private bool _canViewUsers; - private bool _canViewProducts; - private bool _canViewBrands; - private bool _canViewTodos; - private bool _canViewTenants; - private bool _canViewAuditTrails; - private bool CanViewAdministrationGroup => _canViewUsers || _canViewRoles || _canViewTenants; - - protected override async Task OnParametersSetAsync() - { - var user = (await AuthState).User; - _canViewHangfire = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Hangfire); - _canViewDashboard = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Dashboard); - _canViewRoles = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Roles); - _canViewUsers = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Users); - _canViewProducts = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Products); - _canViewBrands = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Brands); - _canViewTodos = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Todos); - _canViewTenants = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.Tenants); - _canViewAuditTrails = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.AuditTrails); - } -} diff --git a/src/apps/blazor/client/Layout/NavMenu.razor.css b/src/apps/blazor/client/Layout/NavMenu.razor.css deleted file mode 100644 index 881d128a5f..0000000000 --- a/src/apps/blazor/client/Layout/NavMenu.razor.css +++ /dev/null @@ -1,83 +0,0 @@ -.navbar-toggler { - background-color: rgba(255, 255, 255, 0.1); -} - -.top-row { - height: 3.5rem; - background-color: rgba(0,0,0,0.4); -} - -.navbar-brand { - font-size: 1.1rem; -} - -.bi { - display: inline-block; - position: relative; - width: 1.25rem; - height: 1.25rem; - margin-right: 0.75rem; - top: -1px; - background-size: cover; -} - -.bi-house-door-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); -} - -.bi-plus-square-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); -} - -.bi-list-nested-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); -} - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item ::deep a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - -.nav-item ::deep a.active { - background-color: rgba(255,255,255,0.37); - color: white; -} - -.nav-item ::deep a:hover { - background-color: rgba(255,255,255,0.1); - color: white; -} - -@media (min-width: 641px) { - .navbar-toggler { - display: none; - } - - .collapse { - /* Never collapse the sidebar for wide screens */ - display: block; - } - - .nav-scrollable { - /* Allow sidebar to scroll for tall menus */ - height: calc(100vh - 3.5rem); - overflow-y: auto; - } -} diff --git a/src/apps/blazor/client/Layout/NotFound.razor b/src/apps/blazor/client/Layout/NotFound.razor deleted file mode 100644 index 6fe4b2ce57..0000000000 --- a/src/apps/blazor/client/Layout/NotFound.razor +++ /dev/null @@ -1,47 +0,0 @@ -@inherits LayoutComponentBase - - - - -
- - - - - - - - - - - - - - - - - Not Found -
- Go Home -
-
-
- -@code{ - -} \ No newline at end of file diff --git a/src/apps/blazor/client/Layout/NotFound.razor.cs b/src/apps/blazor/client/Layout/NotFound.razor.cs deleted file mode 100644 index 674565ed3e..0000000000 --- a/src/apps/blazor/client/Layout/NotFound.razor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Themes; -using FSH.Starter.Blazor.Infrastructure.Preferences; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Layout; - -public partial class NotFound -{ - private ClientPreference? _themePreference; - private MudTheme _theme = new FshTheme(); - private bool _isDarkMode; - - protected override async Task OnInitializedAsync() - { - _themePreference = await ClientPreferences.GetPreference() as ClientPreference; - if (_themePreference == null) _themePreference = new ClientPreference(); - SetCurrentTheme(_themePreference); - } - - private void SetCurrentTheme(ClientPreference themePreference) - { - _isDarkMode = themePreference.IsDarkMode; - //_currentTheme = new FshTheme(); - //if (themePreference.IsDarkMode) - //{ - // _currentTheme. - //} - //_currentTheme.Palette.Primary = themePreference.PrimaryColor; - //_currentTheme.Palette.Secondary = themePreference.SecondaryColor; - //_currentTheme.LayoutProperties.DefaultBorderRadius = $"{themePreference.BorderRadius}px"; - //_currentTheme.LayoutProperties.DefaultBorderRadius = $"{themePreference.BorderRadius}px"; - //_rightToLeft = themePreference.IsRTL; - } -} diff --git a/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor b/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor deleted file mode 100644 index e2b35c86af..0000000000 --- a/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor +++ /dev/null @@ -1,41 +0,0 @@ -@page "/forgot-password" -@attribute [AllowAnonymous] - -Forgot Password - - - - - - - - -
- Forgot Password? - - We can help you by resetting your password. -
-
-
- - - - - - - - - - - - - - - Forgot Password - -
-
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor.cs b/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor.cs deleted file mode 100644 index 419185b954..0000000000 --- a/src/apps/blazor/client/Pages/Auth/ForgotPassword.razor.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Pages.Auth; - -public partial class ForgotPassword -{ - private readonly ForgotPasswordCommand _forgotPasswordRequest = new(); - private FshValidation? _customValidation; - private bool BusySubmitting { get; set; } - - [Inject] - private IApiClient UsersClient { get; set; } = default!; - - private string Tenant { get; set; } = TenantConstants.Root.Id; - - private async Task SubmitAsync() - { - BusySubmitting = true; - - await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.ForgotPasswordEndpointAsync(Tenant, _forgotPasswordRequest), - Toast, - _customValidation); - - BusySubmitting = false; - } -} diff --git a/src/apps/blazor/client/Pages/Auth/Login.razor b/src/apps/blazor/client/Pages/Auth/Login.razor deleted file mode 100644 index ed00d61dba..0000000000 --- a/src/apps/blazor/client/Pages/Auth/Login.razor +++ /dev/null @@ -1,46 +0,0 @@ -@page "/login" -@attribute [AllowAnonymous] -@inject IAuthenticationService authService - -Login - -
- Sign In - - Enter your credentials to get started. - -
-
- - - - - - - - - - - - - - - - Register? - - - Forgot password? - - - Sign In - - - Fill Administrator Credentials - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Auth/Login.razor.cs b/src/apps/blazor/client/Pages/Auth/Login.razor.cs deleted file mode 100644 index 1bd6d7bff1..0000000000 --- a/src/apps/blazor/client/Pages/Auth/Login.razor.cs +++ /dev/null @@ -1,71 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Auth; - -public partial class Login() -{ - [CascadingParameter] - public Task AuthState { get; set; } = default!; - - private FshValidation? _customValidation; - - public bool BusySubmitting { get; set; } - - private readonly TokenGenerationCommand _tokenRequest = new(); - private string TenantId { get; set; } = string.Empty; - private bool _passwordVisibility; - private InputType _passwordInput = InputType.Password; - private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - - protected override async Task OnInitializedAsync() - { - var authState = await AuthState; - if (authState.User.Identity?.IsAuthenticated is true) - { - Navigation.NavigateTo("/"); - } - } - - private void TogglePasswordVisibility() - { - if (_passwordVisibility) - { - _passwordVisibility = false; - _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - _passwordInput = InputType.Password; - } - else - { - _passwordVisibility = true; - _passwordInputIcon = Icons.Material.Filled.Visibility; - _passwordInput = InputType.Text; - } - } - - private void FillAdministratorCredentials() - { - _tokenRequest.Email = TenantConstants.Root.EmailAddress; - _tokenRequest.Password = TenantConstants.DefaultPassword; - TenantId = TenantConstants.Root.Id; - } - - private async Task SubmitAsync() - { - BusySubmitting = true; - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => authService.LoginAsync(TenantId, _tokenRequest), - Toast, - _customValidation)) - { - Toast.Add($"Logged in as {_tokenRequest.Email}", Severity.Info); - } - - BusySubmitting = false; - } -} diff --git a/src/apps/blazor/client/Pages/Auth/Logout.razor b/src/apps/blazor/client/Pages/Auth/Logout.razor deleted file mode 100644 index c02ac1597b..0000000000 --- a/src/apps/blazor/client/Pages/Auth/Logout.razor +++ /dev/null @@ -1,11 +0,0 @@ -@page "/logout" -@attribute [AllowAnonymous] -@inject IAuthenticationService AuthService - -@code{ - protected override async Task OnInitializedAsync() - { - await AuthService.LogoutAsync(); - Toast.Add("Logged out", Severity.Error); - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Auth/SelfRegister.razor b/src/apps/blazor/client/Pages/Auth/SelfRegister.razor deleted file mode 100644 index 82f71886ce..0000000000 --- a/src/apps/blazor/client/Pages/Auth/SelfRegister.razor +++ /dev/null @@ -1,71 +0,0 @@ -@page "/register" -@attribute [AllowAnonymous] - -Register - - - - - - - - -
-
- - Register - - Enter your details below to set up your new account -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Register - - -
-
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Auth/SelfRegister.razor.cs b/src/apps/blazor/client/Pages/Auth/SelfRegister.razor.cs deleted file mode 100644 index 1659c773ab..0000000000 --- a/src/apps/blazor/client/Pages/Auth/SelfRegister.razor.cs +++ /dev/null @@ -1,57 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Auth; - -public partial class SelfRegister -{ - private readonly RegisterUserCommand _createUserRequest = new(); - private FshValidation? _customValidation; - private bool BusySubmitting { get; set; } - - [Inject] - private IApiClient UsersClient { get; set; } = default!; - - private string Tenant { get; set; } = TenantConstants.Root.Id; - - private bool _passwordVisibility; - private InputType _passwordInput = InputType.Password; - private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - - private async Task SubmitAsync() - { - BusySubmitting = true; - - var response = await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.SelfRegisterUserEndpointAsync(Tenant, _createUserRequest), - Toast, Navigation, - _customValidation); - - if (response != null) - { - Toast.Add($"user {response.UserId} registered.", Severity.Success); - Navigation.NavigateTo("/login"); - } - - BusySubmitting = false; - } - - private void TogglePasswordVisibility() - { - if (_passwordVisibility) - { - _passwordVisibility = false; - _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - _passwordInput = InputType.Password; - } - else - { - _passwordVisibility = true; - _passwordInputIcon = Icons.Material.Filled.Visibility; - _passwordInput = InputType.Text; - } - } -} diff --git a/src/apps/blazor/client/Pages/Catalog/Brands.razor b/src/apps/blazor/client/Pages/Catalog/Brands.razor deleted file mode 100644 index e805ff3798..0000000000 --- a/src/apps/blazor/client/Pages/Catalog/Brands.razor +++ /dev/null @@ -1,44 +0,0 @@ -@page "/catalog/brands" - - - - - - - - - - - @if (!Context.AddEditModal.IsCreate) - { - - - - } - - - - - - - - -
- @if(!Context.AddEditModal.IsCreate) - { - - View - - - - Delete - - } -
-
-
-
- -
diff --git a/src/apps/blazor/client/Pages/Catalog/Brands.razor.cs b/src/apps/blazor/client/Pages/Catalog/Brands.razor.cs deleted file mode 100644 index 846f2985f4..0000000000 --- a/src/apps/blazor/client/Pages/Catalog/Brands.razor.cs +++ /dev/null @@ -1,50 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Pages.Catalog; - -public partial class Brands -{ - [Inject] - protected IApiClient _client { get; set; } = default!; - - protected EntityServerTableContext Context { get; set; } = default!; - - private EntityTable _table = default!; - - protected override void OnInitialized() => - Context = new( - entityName: "Brand", - entityNamePlural: "Brands", - entityResource: FshResources.Brands, - fields: new() - { - new(brand => brand.Id, "Id", "Id"), - new(brand => brand.Name, "Name", "Name"), - new(brand => brand.Description, "Description", "Description") - }, - enableAdvancedSearch: true, - idFunc: brand => brand.Id!.Value, - searchFunc: async filter => - { - var brandFilter = filter.Adapt(); - var result = await _client.SearchBrandsEndpointAsync("1", brandFilter); - return result.Adapt>(); - }, - createFunc: async brand => - { - await _client.CreateBrandEndpointAsync("1", brand.Adapt()); - }, - updateFunc: async (id, brand) => - { - await _client.UpdateBrandEndpointAsync("1", id, brand.Adapt()); - }, - deleteFunc: async id => await _client.DeleteBrandEndpointAsync("1", id)); -} - -public class BrandViewModel : UpdateBrandCommand -{ -} diff --git a/src/apps/blazor/client/Pages/Catalog/Products.razor b/src/apps/blazor/client/Pages/Catalog/Products.razor deleted file mode 100644 index f3cb893b1d..0000000000 --- a/src/apps/blazor/client/Pages/Catalog/Products.razor +++ /dev/null @@ -1,64 +0,0 @@ -@page "/catalog/products" - - - - - - - - All Brands - @foreach (var brand in _brands) - { - @brand.Name - } - - Minimum Rate: @_searchMinimumRate.ToString() - Maximum Rate: @_searchMaximumRate.ToString() - - - - @if (!Context.AddEditModal.IsCreate) - { - - - - } - - - - - - - - - - - - @foreach (var brand in _brands) - { - @brand.Name - } - - - - -
- @if(!Context.AddEditModal.IsCreate) - { - - View - - - - Delete - - } -
-
-
-
- -
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Catalog/Products.razor.cs b/src/apps/blazor/client/Pages/Catalog/Products.razor.cs deleted file mode 100644 index 46266197cd..0000000000 --- a/src/apps/blazor/client/Pages/Catalog/Products.razor.cs +++ /dev/null @@ -1,108 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Pages.Catalog; - -public partial class Products -{ - [Inject] - protected IApiClient _client { get; set; } = default!; - - protected EntityServerTableContext Context { get; set; } = default!; - - private EntityTable _table = default!; - - private List _brands = new(); - - protected override async Task OnInitializedAsync() - { - Context = new( - entityName: "Product", - entityNamePlural: "Products", - entityResource: FshResources.Products, - fields: new() - { - new(prod => prod.Id,"Id", "Id"), - new(prod => prod.Name,"Name", "Name"), - new(prod => prod.Description, "Description", "Description"), - new(prod => prod.Price, "Price", "Price"), - new(prod => prod.Brand?.Name, "Brand", "Brand") - }, - enableAdvancedSearch: true, - idFunc: prod => prod.Id!.Value, - searchFunc: async filter => - { - var productFilter = filter.Adapt(); - productFilter.MinimumRate = Convert.ToDouble(SearchMinimumRate); - productFilter.MaximumRate = Convert.ToDouble(SearchMaximumRate); - productFilter.BrandId = SearchBrandId; - var result = await _client.SearchProductsEndpointAsync("1", productFilter); - return result.Adapt>(); - }, - createFunc: async prod => - { - await _client.CreateProductEndpointAsync("1", prod.Adapt()); - }, - updateFunc: async (id, prod) => - { - await _client.UpdateProductEndpointAsync("1", id, prod.Adapt()); - }, - deleteFunc: async id => await _client.DeleteProductEndpointAsync("1", id)); - - await LoadBrandsAsync(); - } - - private async Task LoadBrandsAsync() - { - if (_brands.Count == 0) - { - var response = await _client.SearchBrandsEndpointAsync("1", new SearchBrandsCommand()); - if (response?.Items != null) - { - _brands = response.Items.ToList(); - } - } - } - - // Advanced Search - - private Guid? _searchBrandId; - private Guid? SearchBrandId - { - get => _searchBrandId; - set - { - _searchBrandId = value; - _ = _table.ReloadDataAsync(); - } - } - - private decimal _searchMinimumRate; - private decimal SearchMinimumRate - { - get => _searchMinimumRate; - set - { - _searchMinimumRate = value; - _ = _table.ReloadDataAsync(); - } - } - - private decimal _searchMaximumRate = 9999; - private decimal SearchMaximumRate - { - get => _searchMaximumRate; - set - { - _searchMaximumRate = value; - _ = _table.ReloadDataAsync(); - } - } -} - -public class ProductViewModel : UpdateProductCommand -{ -} diff --git a/src/apps/blazor/client/Pages/Counter.razor b/src/apps/blazor/client/Pages/Counter.razor deleted file mode 100644 index e1ba5df4d9..0000000000 --- a/src/apps/blazor/client/Pages/Counter.razor +++ /dev/null @@ -1,18 +0,0 @@ -@page "/counter" - -Counter - -Counter - -Current count: @currentCount - -Click me - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/src/apps/blazor/client/Pages/Home.razor b/src/apps/blazor/client/Pages/Home.razor deleted file mode 100644 index 4ab0981f44..0000000000 --- a/src/apps/blazor/client/Pages/Home.razor +++ /dev/null @@ -1,95 +0,0 @@ -@page "/" -@using System.Security.Claims - - - - -
- -
-
- - The best way to start a fullstack .NET 9 Web App. - - - - fullstackhero's - .NET 9 Starter Kit - - - - - Built with the goodness of MudBlazor Component Library - - - - -
- Get Started - Star on GitHub -
-
- - Version 2.0 - - - - - In case you are stuck anywhere or have any queries regarding this implementation, I have compiled a Quick Start Guide for you reference. - Read The Guide - - - - - - - - - Here are few articles that should help you get started with Blazor. - - - - - - - - - - - - Application Claims of the currently logged in user - - @if (Claims is not null) - { - @foreach (var claim in Claims) - { - - - @claim.Type - - @claim.Value - - } - } - - - - - - Liked this Boilerplate? Star us on Github! - -
-
- -@code { - [CascadingParameter] - public Task AuthState { get; set; } = default!; - - public IEnumerable? Claims { get; set; } - - protected override async Task OnInitializedAsync() - { - var authState = await AuthState; - Claims = authState.User.Claims; - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Account/Account.razor b/src/apps/blazor/client/Pages/Identity/Account/Account.razor deleted file mode 100644 index 0e7f7d13a3..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Account/Account.razor +++ /dev/null @@ -1,31 +0,0 @@ -@page "/identity/account" - - - - - - - - @if (!SecurityTabHidden) - { - - - - } - - -@code -{ - [Inject] - public IAuthenticationService AuthService { get; set; } = default!; - - public bool SecurityTabHidden { get; set; } = false; - - protected override void OnInitialized() - { - // if (AuthService.ProviderType == AuthProvider.AzureAd) - // { - // SecurityTabHidden = true; - // } - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Account/Profile.razor b/src/apps/blazor/client/Pages/Identity/Account/Profile.razor deleted file mode 100644 index 63ce561c98..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Account/Profile.razor +++ /dev/null @@ -1,84 +0,0 @@ - - - - -
- @if (!string.IsNullOrEmpty(_imageUrl)) - { - - - - } - else - { - @_firstLetterOfName - } -
- @_profileModel.FirstName @_profileModel.LastName - @_profileModel.Email -
- - -
-
- - - - - - Profile Details - - - - - - - - - - - - - - - - - - - - - - Save Changes - - - - -
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Account/Profile.razor.cs b/src/apps/blazor/client/Pages/Identity/Account/Profile.razor.cs deleted file mode 100644 index f979d3f162..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Account/Profile.razor.cs +++ /dev/null @@ -1,102 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Client.Components.Dialogs; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Forms; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Account; - -public partial class Profile -{ - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthenticationService AuthService { get; set; } = default!; - [Inject] - protected IApiClient PersonalClient { get; set; } = default!; - - private readonly UpdateUserCommand _profileModel = new(); - - private string? _imageUrl; - private string? _userId; - private char _firstLetterOfName; - - private FshValidation? _customValidation; - - protected override async Task OnInitializedAsync() - { - if ((await AuthState).User is { } user) - { - _userId = user.GetUserId(); - _profileModel.Email = user.GetEmail() ?? string.Empty; - _profileModel.FirstName = user.GetFirstName() ?? string.Empty; - _profileModel.LastName = user.GetSurname() ?? string.Empty; - _profileModel.PhoneNumber = user.GetPhoneNumber(); - if (user.GetImageUrl() != null) - { - _imageUrl = user.GetImageUrl()!.ToString(); - } - if (_userId is not null) _profileModel.Id = _userId; - } - - if (_profileModel.FirstName?.Length > 0) - { - _firstLetterOfName = _profileModel.FirstName.ToUpper().FirstOrDefault(); - } - } - - private async Task UpdateProfileAsync() - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => PersonalClient.UpdateUserEndpointAsync(_profileModel), Toast, _customValidation)) - { - Toast.Add("Your Profile has been updated. Please Login again to Continue.", Severity.Success); - await AuthService.ReLoginAsync(Navigation.Uri); - } - } - - private async Task UploadFiles(InputFileChangeEventArgs e) - { - var file = e.File; - if (file is not null) - { - string? extension = Path.GetExtension(file.Name); - if (!AppConstants.SupportedImageFormats.Contains(extension.ToLower())) - { - Toast.Add("Image Format Not Supported.", Severity.Error); - return; - } - - string? fileName = $"{_userId}-{Guid.NewGuid():N}"; - fileName = fileName[..Math.Min(fileName.Length, 90)]; - var imageFile = await file.RequestImageFileAsync(AppConstants.StandardImageFormat, AppConstants.MaxImageWidth, AppConstants.MaxImageHeight); - byte[]? buffer = new byte[imageFile.Size]; - await imageFile.OpenReadStream(AppConstants.MaxAllowedSize).ReadAsync(buffer); - string? base64String = $"data:{AppConstants.StandardImageFormat};base64,{Convert.ToBase64String(buffer)}"; - _profileModel.Image = new FileUploadCommand() { Name = fileName, Data = base64String, Extension = extension }; - - await UpdateProfileAsync(); - } - } - - public async Task RemoveImageAsync() - { - string deleteContent = "You're sure you want to delete your Profile Image?"; - var parameters = new DialogParameters - { - { nameof(DeleteConfirmation.ContentText), deleteContent } - }; - var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true, BackdropClick = false }; - var dialog = await DialogService.ShowAsync("Delete", parameters, options); - var result = await dialog.Result; - if (!result!.Canceled) - { - _profileModel.DeleteCurrentImage = true; - await UpdateProfileAsync(); - } - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Account/Security.razor b/src/apps/blazor/client/Pages/Identity/Account/Security.razor deleted file mode 100644 index 4e6a3841bb..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Account/Security.razor +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Change Password - - - - - - - - - - - - - - - - - - - - Change Password - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Account/Security.razor.cs b/src/apps/blazor/client/Pages/Identity/Account/Security.razor.cs deleted file mode 100644 index e9dcd200da..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Account/Security.razor.cs +++ /dev/null @@ -1,71 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using Microsoft.AspNetCore.Components; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Account; - -public partial class Security -{ - [Inject] - public IApiClient PersonalClient { get; set; } = default!; - - private readonly ChangePasswordCommand _passwordModel = new(); - - private FshValidation? _customValidation; - - private async Task ChangePasswordAsync() - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => PersonalClient.ChangePasswordEndpointAsync(_passwordModel), - Toast, - _customValidation, - "Password Changed!")) - { - _passwordModel.Password = string.Empty; - _passwordModel.NewPassword = string.Empty; - _passwordModel.ConfirmNewPassword = string.Empty; - } - } - - private bool _currentPasswordVisibility; - private InputType _currentPasswordInput = InputType.Password; - private string _currentPasswordInputIcon = Icons.Material.Filled.VisibilityOff; - private bool _newPasswordVisibility; - private InputType _newPasswordInput = InputType.Password; - private string _newPasswordInputIcon = Icons.Material.Filled.VisibilityOff; - - private void TogglePasswordVisibility(bool newPassword) - { - if (newPassword) - { - if (_newPasswordVisibility) - { - _newPasswordVisibility = false; - _newPasswordInputIcon = Icons.Material.Filled.VisibilityOff; - _newPasswordInput = InputType.Password; - } - else - { - _newPasswordVisibility = true; - _newPasswordInputIcon = Icons.Material.Filled.Visibility; - _newPasswordInput = InputType.Text; - } - } - else - { - if (_currentPasswordVisibility) - { - _currentPasswordVisibility = false; - _currentPasswordInputIcon = Icons.Material.Filled.VisibilityOff; - _currentPasswordInput = InputType.Password; - } - else - { - _currentPasswordVisibility = true; - _currentPasswordInputIcon = Icons.Material.Filled.Visibility; - _currentPasswordInput = InputType.Text; - } - } - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor b/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor deleted file mode 100644 index 0124ba98f9..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor +++ /dev/null @@ -1,73 +0,0 @@ -@page "/identity/roles/{Id}/permissions" - - - -@if (!_loaded) -{ - -} -else -{ - - @foreach (var group in _groupedRoleClaims.Keys) - { - var selectedRoleClaimsInGroup = _groupedRoleClaims[group].Where(c => c.Enabled).ToList(); - var allRoleClaimsInGroup = _groupedRoleClaims[group].ToList(); - - - -
- - Back - - @if (_canEditRoleClaims) - { - Update Permissions - - } -
- - @if (_canSearchRoleClaims) - { - - - } -
- - - - Permission Name - - - - Description - - - Status - - - - - - - - - - - - - - - - - - -
-
- } -
-} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor.cs b/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor.cs deleted file mode 100644 index 9f92a0e566..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Roles/RolePermissions.razor.cs +++ /dev/null @@ -1,109 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Roles; - -public partial class RolePermissions -{ - [Parameter] - public string Id { get; set; } = default!; // from route - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - [Inject] - protected IApiClient RolesClient { get; set; } = default!; - - private Dictionary> _groupedRoleClaims = default!; - - public string _title = string.Empty; - public string _description = string.Empty; - - private string _searchString = string.Empty; - - private bool _canEditRoleClaims; - private bool _canSearchRoleClaims; - private bool _loaded; - - static RolePermissions() => TypeAdapterConfig.NewConfig().MapToConstructor(true); - - protected override async Task OnInitializedAsync() - { - var state = await AuthState; - - _canEditRoleClaims = await AuthService.HasPermissionAsync(state.User, FshActions.Update, FshResources.RoleClaims); - _canSearchRoleClaims = await AuthService.HasPermissionAsync(state.User, FshActions.View, FshResources.RoleClaims); - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => RolesClient.GetRolePermissionsEndpointAsync(Id), Toast, Navigation) - is RoleDto role && role.Permissions is not null) - { - _title = string.Format("{0} Permissions", role.Name); - _description = string.Format("Manage {0} Role Permissions", role.Name); - - var permissions = state.User.GetTenant() == TenantConstants.Root.Id - ? FshPermissions.All - : FshPermissions.Admin; - - _groupedRoleClaims = permissions - .GroupBy(p => p.Resource) - .ToDictionary(g => g.Key, g => g.Select(p => - { - var permission = p.Adapt(); - permission.Enabled = role.Permissions.Contains(permission.Name); - return permission; - }).ToList()); - } - - _loaded = true; - } - - private Color GetGroupBadgeColor(int selected, int all) - { - if (selected == 0) - return Color.Error; - - if (selected == all) - return Color.Success; - - return Color.Info; - } - - private async Task SaveAsync() - { - var allPermissions = _groupedRoleClaims.Values.SelectMany(a => a); - var selectedPermissions = allPermissions.Where(a => a.Enabled); - var request = new UpdatePermissionsCommand() - { - RoleId = Id, - Permissions = selectedPermissions.Where(x => x.Enabled).Select(x => x.Name).ToList(), - }; - await ApiHelper.ExecuteCallGuardedAsync( - () => RolesClient.UpdateRolePermissionsEndpointAsync(request.RoleId, request), - Toast, - successMessage: "Updated Permissions."); - Navigation.NavigateTo("/identity/roles"); - } - - private bool Search(PermissionViewModel permission) => - string.IsNullOrWhiteSpace(_searchString) - || permission.Name.Contains(_searchString, StringComparison.OrdinalIgnoreCase) is true - || permission.Description.Contains(_searchString, StringComparison.OrdinalIgnoreCase) is true; -} - -public record PermissionViewModel : FshPermission -{ - public bool Enabled { get; set; } - - public PermissionViewModel(string Description, string Action, string Resource, bool IsBasic = false, bool IsRoot = false) - : base(Description, Action, Resource, IsBasic, IsRoot) - { - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor b/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor deleted file mode 100644 index 55f909cdbd..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor +++ /dev/null @@ -1,28 +0,0 @@ -@page "/identity/roles" - - - - - - @if (_canViewRoleClaims) - { - Manage Permission - } - - - @if (!Context.AddEditModal.IsCreate) - { - - - - } - - - - - - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor.cs b/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor.cs deleted file mode 100644 index 848384182c..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Roles/Roles.razor.cs +++ /dev/null @@ -1,60 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Roles; - -public partial class Roles -{ - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - [Inject] - private IApiClient RolesClient { get; set; } = default!; - - protected EntityClientTableContext Context { get; set; } = default!; - - private bool _canViewRoleClaims; - - protected override async Task OnInitializedAsync() - { - var state = await AuthState; - _canViewRoleClaims = await AuthService.HasPermissionAsync(state.User, FshActions.View, FshResources.RoleClaims); - - Context = new( - entityName: "Role", - entityNamePlural: "Roles", - entityResource: FshResources.Roles, - searchAction: FshActions.View, - fields: new() - { - new(role => role.Id, "Id"), - new(role => role.Name,"Name"), - new(role => role.Description, "Description") - }, - idFunc: role => role.Id, - loadDataFunc: async () => (await RolesClient.GetRolesEndpointAsync()).ToList(), - searchFunc: (searchString, role) => - string.IsNullOrWhiteSpace(searchString) - || role.Name?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || role.Description?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true, - createFunc: async role => await RolesClient.CreateOrUpdateRoleEndpointAsync(role), - updateFunc: async (_, role) => await RolesClient.CreateOrUpdateRoleEndpointAsync(role), - deleteFunc: async id => await RolesClient.DeleteRoleEndpointAsync(id!), - hasExtraActionsFunc: () => _canViewRoleClaims, - canUpdateEntityFunc: e => !FshRoles.IsDefault(e.Name!), - canDeleteEntityFunc: e => !FshRoles.IsDefault(e.Name!), - exportAction: string.Empty); - } - - private void ManagePermissions(string? roleId) - { - ArgumentNullException.ThrowIfNull(roleId, nameof(roleId)); - Navigation.NavigateTo($"/identity/roles/{roleId}/permissions"); - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Users/Audit.razor b/src/apps/blazor/client/Pages/Identity/Users/Audit.razor deleted file mode 100644 index 8da26afad7..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/Audit.razor +++ /dev/null @@ -1,122 +0,0 @@ -@page "/identity/users/{Id:guid}/audit-trail" -@page "/identity/audit-trail" - - - - - - - - @((context.ShowDetails == true) ? "Hide" : "Show") Trail Details - - - - @if (context.ShowDetails) - { - - - - - - Details for Audit Trail with Id : @context.Id - - - - - - @if (!string.IsNullOrEmpty(context.ModifiedProperties)) - { - - - - - } - @if (!string.IsNullOrEmpty(context.PrimaryKey)) - { - - - - - } - @if (!string.IsNullOrEmpty(context.PreviousValues)) - { - - - - - } - @if (!string.IsNullOrEmpty(context.NewValues)) - { - - - - - } - -
Modified Properties - - @foreach (var column in context.ModifiedProperties.Trim('[').Trim(']').Split(',')) - { - @column.Replace('"', ' ').Trim() - } - -
Primary Key - - @context.PrimaryKey?.Trim('{').Trim('}').Replace('"', ' ').Trim() - -
Previous Values - - - @foreach (var value in context.PreviousValues.Trim('{').Trim('}').Split(',')) - { - @if (_searchInOldValues) - { - - - - } - else - { - @value.Replace('"', ' ').Trim() - } - } - -
Current Values - - - @foreach (var value in context.NewValues.Trim('{').Trim('}').Split(',')) - { - @if (_searchInNewValues) - { - - - - } - else - { - @value.Replace('"', ' ').Trim() - } - } - -
-
-
- -
- } -
- -
- -@code { - private RenderFragment DateFieldTemplate => trail => __builder => - { - - @trail.DateTime.ToString("dd-MMMM-yyyy hh:mm tt") - - - @trail.UTCTime.ToString("dd-MMMM-yyyy hh:mm tt") (UTC) - - }; -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Users/Audit.razor.cs b/src/apps/blazor/client/Pages/Identity/Users/Audit.razor.cs deleted file mode 100644 index 89b6a39923..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/Audit.razor.cs +++ /dev/null @@ -1,89 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Users; - -public partial class Audit -{ - [Inject] - private IApiClient ApiClient { get; set; } = default!; - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Parameter] - public Guid Id { get; set; } - - protected EntityClientTableContext Context { get; set; } = default!; - - private string? _searchString; - private string? _subHeader; - private MudDateRangePicker _dateRangePicker = default!; - private DateRange? _dateRange; - private bool _searchInOldValues; - private bool _searchInNewValues; - private List _trails = new(); - - // Configure Automapper - static Audit() => - TypeAdapterConfig.NewConfig().Map( - dest => dest.UTCTime, - src => DateTime.SpecifyKind(src.DateTime, DateTimeKind.Utc).ToLocalTime()); - - - - protected override async Task OnInitializedAsync() - { - if (Id == Guid.Empty) - { - var state = await AuthState; - if (state != null) - { - Id = new Guid(state.User.GetUserId()!); - } - } - _subHeader = $"Audit Trail for User {Id}"; - Context = new( - entityNamePlural: "Trails", - searchAction: true.ToString(), - fields: new() - { - new(audit => audit.Id,"Id"), - new(audit => audit.Entity, "Entity"), - new(audit => audit.DateTime, "Date", Template: DateFieldTemplate), - new(audit => audit.Operation, "Operation") - }, - loadDataFunc: async () => _trails = (await ApiClient.GetUserAuditTrailEndpointAsync(Id)).Adapt>(), - searchFunc: (searchString, trail) => - (string.IsNullOrWhiteSpace(searchString) // check Search String - || trail.Entity?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || (_searchInOldValues && - trail.PreviousValues?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true) - || (_searchInNewValues && - trail.NewValues?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true)) - && ((_dateRange?.Start is null && _dateRange?.End is null) // check Date Range - || (_dateRange?.Start is not null && _dateRange.End is null && trail.DateTime >= _dateRange.Start) - || (_dateRange?.Start is null && _dateRange?.End is not null && trail.DateTime <= _dateRange.End + new TimeSpan(0, 11, 59, 59, 999)) - || (trail.DateTime >= _dateRange!.Start && trail.DateTime <= _dateRange.End + new TimeSpan(0, 11, 59, 59, 999))), - hasExtraActionsFunc: () => true); - } - - private void ShowBtnPress(Guid id) - { - var trail = _trails.First(f => f.Id == id); - trail.ShowDetails = !trail.ShowDetails; - foreach (var otherTrail in _trails.Except(new[] { trail })) - { - otherTrail.ShowDetails = false; - } - } - - public class RelatedAuditTrail : AuditTrail - { - public bool ShowDetails { get; set; } - public DateTime UTCTime { get; set; } - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor b/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor deleted file mode 100644 index 6da18c6e3e..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor +++ /dev/null @@ -1,147 +0,0 @@ -@page "/identity/users/{Id}/profile" - - - -@if (!_loaded) -{ - -} -else -{ - - - @if (_canToggleUserStatus) - { - - - - - Administrator Settings. - This is an Administrator Only View. - - - - - - - Save Changes - - - - - - } - - - - -
- @if (_imageUrl != null) - { - - - - } - else - { - @_firstLetterOfName - - } -
- @_firstName @_lastName - @_email -
- -
- @if (_imageUrl != null) - { - - View - - } -
- -
-
-
- - - - - Public Profile - - - - - - @_firstName - - - @_lastName - - - @_phoneNumber - - - - @_email - - - - - -
-} - -@code -{ -public class CustomStringToBoolConverter : BoolConverter - { - - public CustomStringToBoolConverter() - { - SetFunc = OnSet; - GetFunc = OnGet; - } - private string TrueString = "User Active"; - private string FalseString = "no, at all"; - private string NullString = "I don't know"; - - private string OnGet(bool? value) - { - try - { - return (value == true) ? TrueString : FalseString; - } - catch (Exception e) - { - UpdateGetError("Conversion error: " + e.Message); - return NullString; - } - } - - private bool? OnSet(string arg) - { - if (arg == null) - return null; - try - { - if (arg == TrueString) - return true; - if (arg == FalseString) - return false; - else - return null; - } - catch (FormatException e) - { - UpdateSetError("Conversion error: " + e.Message); - return null; - } - } - - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor.cs b/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor.cs deleted file mode 100644 index e46bc35faa..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/UserProfile.razor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Users; - -public partial class UserProfile -{ - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - [Inject] - protected IApiClient UsersClient { get; set; } = default!; - - [Parameter] - public string? Id { get; set; } - [Parameter] - public string? Title { get; set; } - [Parameter] - public string? Description { get; set; } - - private bool _active; - private bool _emailConfirmed; - private char _firstLetterOfName; - private string? _firstName; - private string? _lastName; - private string? _phoneNumber; - private string? _email; - private Uri? _imageUrl; - private bool _loaded; - private bool _canToggleUserStatus; - - private async Task ToggleUserStatus() - { - var request = new ToggleUserStatusCommand { ActivateUser = _active, UserId = Id }; - await ApiHelper.ExecuteCallGuardedAsync(() => UsersClient.ToggleUserStatusEndpointAsync(Id!, request), Toast); - Navigation.NavigateTo("/identity/users"); - } - - [Parameter] - public string? ImageUrl { get; set; } - - protected override async Task OnInitializedAsync() - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.GetUserEndpointAsync(Id!), Toast, Navigation) - is UserDetail user) - { - _firstName = user.FirstName; - _lastName = user.LastName; - _email = user.Email; - _phoneNumber = user.PhoneNumber; - _active = user.IsActive; - _emailConfirmed = user.EmailConfirmed; - _imageUrl = user.ImageUrl; - Title = $"{_firstName} {_lastName}'s Profile"; - Description = _email; - if (_firstName?.Length > 0) - { - _firstLetterOfName = _firstName.ToUpperInvariant().FirstOrDefault(); - } - } - - var state = await AuthState; - _canToggleUserStatus = await AuthService.HasPermissionAsync(state.User, FshActions.Update, FshResources.Users); - _loaded = true; - } -} diff --git a/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor b/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor deleted file mode 100644 index 9684ead141..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor +++ /dev/null @@ -1,66 +0,0 @@ -@page "/identity/users/{Id}/roles" - - - -@if (!_loaded) -{ - -} -else -{ - - -
- - Back - - @if (_canEditUsers) - { - - Update - - } -
- - @if (_canSearchRoles) - { - - - } -
- - - Role Name - - - - - Description - - - - - Status - - - - - - - - - - - - - - - - - - -
-} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor.cs b/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor.cs deleted file mode 100644 index 66972a46a2..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/UserRoles.razor.cs +++ /dev/null @@ -1,78 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Users; - -public partial class UserRoles -{ - [Parameter] - public string? Id { get; set; } - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - [Inject] - protected IApiClient UsersClient { get; set; } = default!; - - private List _userRolesList = default!; - - private string _title = string.Empty; - private string _description = string.Empty; - - private string _searchString = string.Empty; - - private bool _canEditUsers; - private bool _canSearchRoles; - private bool _loaded; - - protected override async Task OnInitializedAsync() - { - var state = await AuthState; - - _canEditUsers = await AuthService.HasPermissionAsync(state.User, FshActions.Update, FshResources.Users); - _canSearchRoles = await AuthService.HasPermissionAsync(state.User, FshActions.View, FshResources.UserRoles); - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.GetUserEndpointAsync(Id!), Toast, Navigation) - is UserDetail user) - { - _title = $"{user.FirstName} {user.LastName}'s Roles"; - _description = string.Format("Manage {0} {1}'s Roles", user.FirstName, user.LastName); - - if (await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.GetUserRolesEndpointAsync(user.Id.ToString()), Toast, Navigation) - is ICollection response) - { - _userRolesList = response.ToList(); - } - } - - _loaded = true; - } - - private async Task SaveAsync() - { - var request = new AssignUserRoleCommand() - { - UserRoles = _userRolesList - }; - - Console.WriteLine($"roles : {request.UserRoles.Count}"); - - await ApiHelper.ExecuteCallGuardedAsync( - () => UsersClient.AssignRolesToUserEndpointAsync(Id, request), - Toast, - successMessage: "updated user roles"); - - Navigation.NavigateTo("/identity/users"); - } - - private bool Search(UserRoleDetail userRole) => - string.IsNullOrWhiteSpace(_searchString) - || userRole.RoleName?.Contains(_searchString, StringComparison.OrdinalIgnoreCase) is true; -} diff --git a/src/apps/blazor/client/Pages/Identity/Users/Users.razor b/src/apps/blazor/client/Pages/Identity/Users/Users.razor deleted file mode 100644 index 1986717c19..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/Users.razor +++ /dev/null @@ -1,47 +0,0 @@ -@page "/identity/users" - - - - - - View Profile - @if (_canViewRoles) - { - Manage Roles - } - @if (_canViewAuditTrails) - { - View Audit Trails - } - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Identity/Users/Users.razor.cs b/src/apps/blazor/client/Pages/Identity/Users/Users.razor.cs deleted file mode 100644 index 24da0adff1..0000000000 --- a/src/apps/blazor/client/Pages/Identity/Users/Users.razor.cs +++ /dev/null @@ -1,99 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Identity.Users; - -public partial class Users -{ - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - - [Inject] - protected IApiClient UsersClient { get; set; } = default!; - - protected EntityClientTableContext Context { get; set; } = default!; - - private bool _canExportUsers; - private bool _canViewAuditTrails; - private bool _canViewRoles; - - // Fields for editform - protected string Password { get; set; } = string.Empty; - protected string ConfirmPassword { get; set; } = string.Empty; - - private bool _passwordVisibility; - private InputType _passwordInput = InputType.Password; - private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - - protected override async Task OnInitializedAsync() - { - var user = (await AuthState).User; - _canExportUsers = await AuthService.HasPermissionAsync(user, FshActions.Export, FshResources.Users); - _canViewRoles = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.UserRoles); - _canViewAuditTrails = await AuthService.HasPermissionAsync(user, FshActions.View, FshResources.AuditTrails); - - Context = new( - entityName: "User", - entityNamePlural: "Users", - entityResource: FshResources.Users, - searchAction: FshActions.View, - updateAction: string.Empty, - deleteAction: string.Empty, - fields: new() - { - new(user => user.FirstName,"First Name"), - new(user => user.LastName, "Last Name"), - new(user => user.UserName, "UserName"), - new(user => user.Email, "Email"), - new(user => user.PhoneNumber, "PhoneNumber"), - new(user => user.EmailConfirmed, "Email Confirmation", Type: typeof(bool)), - new(user => user.IsActive, "Active", Type: typeof(bool)) - }, - idFunc: user => user.Id, - loadDataFunc: async () => (await UsersClient.GetUsersListEndpointAsync()).ToList(), - searchFunc: (searchString, user) => - string.IsNullOrWhiteSpace(searchString) - || user.FirstName?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || user.LastName?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || user.Email?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || user.PhoneNumber?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true - || user.UserName?.Contains(searchString, StringComparison.OrdinalIgnoreCase) == true, - createFunc: user => UsersClient.RegisterUserEndpointAsync(user), - hasExtraActionsFunc: () => true, - exportAction: string.Empty); - } - - private void ViewProfile(in Guid userId) => - Navigation.NavigateTo($"/identity/users/{userId}/profile"); - - private void ManageRoles(in Guid userId) => - Navigation.NavigateTo($"/identity/users/{userId}/roles"); - private void ViewAuditTrails(in Guid userId) => - Navigation.NavigateTo($"/identity/users/{userId}/audit-trail"); - - private void TogglePasswordVisibility() - { - if (_passwordVisibility) - { - _passwordVisibility = false; - _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - _passwordInput = InputType.Password; - } - else - { - _passwordVisibility = true; - _passwordInputIcon = Icons.Material.Filled.Visibility; - _passwordInput = InputType.Text; - } - - Context.AddEditModal.ForceRender(); - } -} diff --git a/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor b/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor deleted file mode 100644 index a51e8e0239..0000000000 --- a/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor +++ /dev/null @@ -1,86 +0,0 @@ -@page "/tenants" - -@inject IAuthenticationService Authentication - - - - - - - - - - - - - - - - - - - - @if(_canUpgrade) - { - Upgrade Subscription - } - - @((context.ShowDetails == true) ? "Hide" : "Show") Tenant Details - - @if (_canModify) - { - @if (!context.IsActive) - { - Activate Tenant - } - else - { - Deactivate Tenant - } - } - - - - @if (context.ShowDetails) - { - - - - - - Details for Tenant : - @context.Id - - - - - - - - @if(string.IsNullOrEmpty(context.ConnectionString?.Trim())) - { - Shared Database - } - else - { - - - } - - -
Connection String - - @context.ConnectionString?.Trim() - -
-
-
- -
- } -
- -
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor.cs b/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor.cs deleted file mode 100644 index 3dbc8d41b4..0000000000 --- a/src/apps/blazor/client/Pages/Multitenancy/Tenants.razor.cs +++ /dev/null @@ -1,121 +0,0 @@ -using FSH.Starter.Blazor.Client.Components; -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using MudBlazor; - -namespace FSH.Starter.Blazor.Client.Pages.Multitenancy; - -public partial class Tenants -{ - [Inject] - private IApiClient ApiClient { get; set; } = default!; - private string? _searchString; - protected EntityClientTableContext Context { get; set; } = default!; - private List _tenants = new(); - public EntityTable EntityTable { get; set; } = default!; - [CascadingParameter] - protected Task AuthState { get; set; } = default!; - [Inject] - protected IAuthorizationService AuthService { get; set; } = default!; - - private bool _canUpgrade; - private bool _canModify; - - protected override async Task OnInitializedAsync() - { - Context = new( - entityName: "Tenant", - entityNamePlural: "Tenants", - entityResource: FshResources.Tenants, - searchAction: FshActions.View, - deleteAction: string.Empty, - updateAction: string.Empty, - fields: new() - { - new(tenant => tenant.Id, "Id"), - new(tenant => tenant.Name, "Name"), - new(tenant => tenant.AdminEmail, "Admin Email"), - new(tenant => tenant.ValidUpto.ToString("MMM dd, yyyy"), "Valid Upto"), - new(tenant => tenant.IsActive, "Active", Type: typeof(bool)) - }, - loadDataFunc: async () => _tenants = (await ApiClient.GetTenantsEndpointAsync()).Adapt>(), - searchFunc: (searchString, tenantDto) => - string.IsNullOrWhiteSpace(searchString) - || tenantDto.Name.Contains(searchString, StringComparison.OrdinalIgnoreCase), - createFunc: tenant => ApiClient.CreateTenantEndpointAsync(tenant.Adapt()), - hasExtraActionsFunc: () => true, - exportAction: string.Empty); - - var state = await AuthState; - _canUpgrade = await AuthService.HasPermissionAsync(state.User, FshActions.UpgradeSubscription, FshResources.Tenants); - _canModify = await AuthService.HasPermissionAsync(state.User, FshActions.Update, FshResources.Tenants); - } - - private void ViewTenantDetails(string id) - { - var tenant = _tenants.First(f => f.Id == id); - tenant.ShowDetails = !tenant.ShowDetails; - foreach (var otherTenants in _tenants.Except(new[] { tenant })) - { - otherTenants.ShowDetails = false; - } - } - - private async Task ViewUpgradeSubscriptionModalAsync(string id) - { - var tenant = _tenants.First(f => f.Id == id); - var parameters = new DialogParameters - { - { - nameof(UpgradeSubscriptionModal.Request), - new UpgradeSubscriptionCommand - { - Tenant = tenant.Id, - ExtendedExpiryDate = tenant.ValidUpto - } - } - }; - var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true, BackdropClick = false }; - var dialog = DialogService.Show("Upgrade Subscription", parameters, options); - var result = await dialog.Result; - if (!result.Canceled) - { - await EntityTable.ReloadDataAsync(); - } - } - - private async Task DeactivateTenantAsync(string id) - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => ApiClient.DisableTenantEndpointAsync(id), - Toast, Navigation, - null, - "Tenant Deactivated.") is not null) - { - await EntityTable.ReloadDataAsync(); - } - } - - private async Task ActivateTenantAsync(string id) - { - if (await ApiHelper.ExecuteCallGuardedAsync( - () => ApiClient.ActivateTenantEndpointAsync(id), - Toast, Navigation, - null, - "Tenant Activated.") is not null) - { - await EntityTable.ReloadDataAsync(); - } - } - - public class TenantViewModel : TenantDetail - { - public bool ShowDetails { get; set; } - } -} diff --git a/src/apps/blazor/client/Pages/Multitenancy/UpgradeSubscriptionModal.razor b/src/apps/blazor/client/Pages/Multitenancy/UpgradeSubscriptionModal.razor deleted file mode 100644 index f91a7b5975..0000000000 --- a/src/apps/blazor/client/Pages/Multitenancy/UpgradeSubscriptionModal.razor +++ /dev/null @@ -1,57 +0,0 @@ -@inject IApiClient TenantsClient - - - - - - - Upgrade Subscription - - - - - - - - - - - - - - - - - Cancel - Upgrade - - - - -@code -{ - [Parameter] public UpgradeSubscriptionCommand Request { get; set; } = new(); - [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!; - DateTime? date = DateTime.Today; - - protected override void OnInitialized() => - date = Request.ExtendedExpiryDate; - - private async Task UpgradeSubscriptionAsync() - { - Request.ExtendedExpiryDate = date.HasValue ? date.Value : Request.ExtendedExpiryDate; - if (await ApiHelper.ExecuteCallGuardedAsync( - () => TenantsClient.UpgradeSubscriptionEndpointAsync(Request), - Toast, Navigation, - null, - "Upgraded Subscription.") is not null) - { - MudDialog.Close(); - } - } - - public void Cancel() - { - MudDialog.Cancel(); - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Todos/Todos.razor b/src/apps/blazor/client/Pages/Todos/Todos.razor deleted file mode 100644 index 9c3ce5d704..0000000000 --- a/src/apps/blazor/client/Pages/Todos/Todos.razor +++ /dev/null @@ -1,39 +0,0 @@ -@page "/todos" - - - - - - @if (!Context.AddEditModal.IsCreate) - { - - - - } - - - - - - - - -
- @if(!Context.AddEditModal.IsCreate) - { - - View - - - - Delete - - } -
-
-
-
- -
\ No newline at end of file diff --git a/src/apps/blazor/client/Pages/Todos/Todos.razor.cs b/src/apps/blazor/client/Pages/Todos/Todos.razor.cs deleted file mode 100644 index dfc6111c5c..0000000000 --- a/src/apps/blazor/client/Pages/Todos/Todos.razor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using FSH.Starter.Blazor.Client.Components.EntityTable; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Shared.Authorization; -using Mapster; -using Microsoft.AspNetCore.Components; - -namespace FSH.Starter.Blazor.Client.Pages.Todos; - -public partial class Todos -{ - [Inject] - protected IApiClient ApiClient { get; set; } = default!; - - protected EntityServerTableContext Context { get; set; } = default!; - - private EntityTable _table = default!; - - protected override void OnInitialized() => - Context = new( - entityName: "Todos", - entityNamePlural: "Todos", - entityResource: FshResources.Todos, - fields: new() - { - new(prod => prod.Id,"Id", "Id"), - new(prod => prod.Title,"Title", "Title"), - new(prod => prod.Note, "Note", "Note") - }, - enableAdvancedSearch: false, - idFunc: prod => prod.Id!.Value, - searchFunc: async filter => - { - var todoFilter = filter.Adapt(); - - var result = await ApiClient.GetTodoListEndpointAsync("1", todoFilter); - return result.Adapt>(); - }, - createFunc: async todo => - { - await ApiClient.CreateTodoEndpointAsync("1", todo.Adapt()); - }, - updateFunc: async (id, todo) => - { - await ApiClient.UpdateTodoEndpointAsync("1", id, todo.Adapt()); - }, - deleteFunc: async id => await ApiClient.DeleteTodoEndpointAsync("1", id)); -} - -public class TodoViewModel : UpdateTodoCommand -{ -} diff --git a/src/apps/blazor/client/Program.cs b/src/apps/blazor/client/Program.cs deleted file mode 100644 index c1026795e2..0000000000 --- a/src/apps/blazor/client/Program.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FSH.Starter.Blazor.Client; -using FSH.Starter.Blazor.Infrastructure; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; - -var builder = WebAssemblyHostBuilder.CreateDefault(args); -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); -builder.Services.AddClientServices(builder.Configuration); - -await builder.Build().RunAsync(); diff --git a/src/apps/blazor/client/Properties/launchSettings.json b/src/apps/blazor/client/Properties/launchSettings.json deleted file mode 100644 index 11f084657d..0000000000 --- a/src/apps/blazor/client/Properties/launchSettings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "https": { - "commandName": "Project", - "dotnetRunMessages": false, - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7100;http://localhost:5100", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/src/apps/blazor/client/_Imports.razor b/src/apps/blazor/client/_Imports.razor deleted file mode 100644 index 198b0183ed..0000000000 --- a/src/apps/blazor/client/_Imports.razor +++ /dev/null @@ -1,34 +0,0 @@ -@using System.Net.Http -@using System.Net.Http.Json -@using FSH.Starter.Blazor.Infrastructure.Preferences -@using Microsoft.AspNetCore.Components.Authorization -@using Microsoft.AspNetCore.Components.Forms -@using Microsoft.AspNetCore.Components.Routing -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.AspNetCore.Components.Web.Virtualization -@using Microsoft.AspNetCore.Components.WebAssembly.Http -@using Microsoft.JSInterop -@using FSH.Starter.Blazor.Client -@using FSH.Starter.Blazor.Client.Layout -@using FSH.Starter.Blazor.Client.Components -@using FSH.Starter.Blazor.Client.Components.General -@using FSH.Starter.Blazor.Client.Components.Dialogs -@using FSH.Starter.Blazor.Client.Components.Common -@using FSH.Starter.Blazor.Client.Components.EntityTable -@using FSH.Starter.Blazor.Infrastructure.Auth -@using MudBlazor -@using Blazored.LocalStorage -@using FSH.Starter.Blazor.Infrastructure.Api -@using FSH.Starter.Blazor.Client.Components.ThemeManager; - -@using FSH.Starter.Blazor.Client.Pages.Auth - -@using Microsoft.AspNetCore.Authorization - -@attribute [Authorize] - -@inject NavigationManager Navigation -@inject ISnackbar Toast -@inject IDialogService DialogService -@inject IConfiguration Config -@inject IClientPreferenceManager ClientPreferences diff --git a/src/apps/blazor/client/wwwroot/appsettings.json b/src/apps/blazor/client/wwwroot/appsettings.json deleted file mode 100644 index f5a8477dca..0000000000 --- a/src/apps/blazor/client/wwwroot/appsettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ApiBaseUrl": "https://localhost:7000/" -} \ No newline at end of file diff --git a/src/apps/blazor/client/wwwroot/appsettings.json.TEMPLATE b/src/apps/blazor/client/wwwroot/appsettings.json.TEMPLATE deleted file mode 100644 index 42be8ab4f6..0000000000 --- a/src/apps/blazor/client/wwwroot/appsettings.json.TEMPLATE +++ /dev/null @@ -1,4 +0,0 @@ -{ - "ApiBaseUrl": "${FSHStarterBlazorClient_ApiBaseUrl}" -} - diff --git a/src/apps/blazor/client/wwwroot/css/fsh.css b/src/apps/blazor/client/wwwroot/css/fsh.css deleted file mode 100644 index 495fdb0900..0000000000 --- a/src/apps/blazor/client/wwwroot/css/fsh.css +++ /dev/null @@ -1,7 +0,0 @@ -.mud-navmenu.mud-navmenu-default .mud-nav-link.active:not(.mud-nav-link-disabled) { - color: inherit; -} - -.mud-list { - border: 1px solid var(--mud-palette-lines-default) -} \ No newline at end of file diff --git a/src/apps/blazor/client/wwwroot/favicon.png b/src/apps/blazor/client/wwwroot/favicon.png deleted file mode 100644 index 8422b59695..0000000000 Binary files a/src/apps/blazor/client/wwwroot/favicon.png and /dev/null differ diff --git a/src/apps/blazor/client/wwwroot/full-stack-hero-logo.png b/src/apps/blazor/client/wwwroot/full-stack-hero-logo.png deleted file mode 100644 index 05bdf45be1..0000000000 Binary files a/src/apps/blazor/client/wwwroot/full-stack-hero-logo.png and /dev/null differ diff --git a/src/apps/blazor/client/wwwroot/icon-192.png b/src/apps/blazor/client/wwwroot/icon-192.png deleted file mode 100644 index 166f56da76..0000000000 Binary files a/src/apps/blazor/client/wwwroot/icon-192.png and /dev/null differ diff --git a/src/apps/blazor/client/wwwroot/icon-512.png b/src/apps/blazor/client/wwwroot/icon-512.png deleted file mode 100644 index c2dd4842dc..0000000000 Binary files a/src/apps/blazor/client/wwwroot/icon-512.png and /dev/null differ diff --git a/src/apps/blazor/client/wwwroot/index.html b/src/apps/blazor/client/wwwroot/index.html deleted file mode 100644 index 8b67346c22..0000000000 --- a/src/apps/blazor/client/wwwroot/index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - FSH.Starter.Blazor - - - - - - - - - - - -
- - -
-
-
-
-
- -
-
-
- -
- An unhandled error has occurred. - Reload - 🗙 -
- - - - - - diff --git a/src/apps/blazor/client/wwwroot/manifest.webmanifest b/src/apps/blazor/client/wwwroot/manifest.webmanifest deleted file mode 100644 index d2a6b40078..0000000000 --- a/src/apps/blazor/client/wwwroot/manifest.webmanifest +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "FSH.Starter.Blazor", - "short_name": "FSH.Starter.Blazor", - "id": "./", - "start_url": "./", - "display": "standalone", - "background_color": "#ffffff", - "theme_color": "#03173d", - "prefer_related_applications": false, - "icons": [ - { - "src": "icon-512.png", - "type": "image/png", - "sizes": "512x512" - }, - { - "src": "icon-192.png", - "type": "image/png", - "sizes": "192x192" - } - ] -} diff --git a/src/apps/blazor/client/wwwroot/service-worker.js b/src/apps/blazor/client/wwwroot/service-worker.js deleted file mode 100644 index fe614daee0..0000000000 --- a/src/apps/blazor/client/wwwroot/service-worker.js +++ /dev/null @@ -1,4 +0,0 @@ -// In development, always fetch from the network and do not enable offline support. -// This is because caching would make development more difficult (changes would not -// be reflected on the first load after each change). -self.addEventListener('fetch', () => { }); diff --git a/src/apps/blazor/client/wwwroot/service-worker.published.js b/src/apps/blazor/client/wwwroot/service-worker.published.js deleted file mode 100644 index 1f7f543fa5..0000000000 --- a/src/apps/blazor/client/wwwroot/service-worker.published.js +++ /dev/null @@ -1,55 +0,0 @@ -// Caution! Be sure you understand the caveats before publishing an application with -// offline support. See https://aka.ms/blazor-offline-considerations - -self.importScripts('./service-worker-assets.js'); -self.addEventListener('install', event => event.waitUntil(onInstall(event))); -self.addEventListener('activate', event => event.waitUntil(onActivate(event))); -self.addEventListener('fetch', event => event.respondWith(onFetch(event))); - -const cacheNamePrefix = 'offline-cache-'; -const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; -const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; -const offlineAssetsExclude = [ /^service-worker\.js$/ ]; - -// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'. -const base = "/"; -const baseUrl = new URL(base, self.origin); -const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href); - -async function onInstall(event) { - console.info('Service worker: Install'); - - // Fetch and cache all matching items from the assets manifest - const assetsRequests = self.assetsManifest.assets - .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) - .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) - .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); - await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); -} - -async function onActivate(event) { - console.info('Service worker: Activate'); - - // Delete unused caches - const cacheKeys = await caches.keys(); - await Promise.all(cacheKeys - .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) - .map(key => caches.delete(key))); -} - -async function onFetch(event) { - let cachedResponse = null; - if (event.request.method === 'GET') { - // For all navigation requests, try to serve index.html from cache, - // unless that request is for an offline resource. - // If you need some URLs to be server-rendered, edit the following check to exclude those URLs - const shouldServeIndexHtml = event.request.mode === 'navigate' - && !manifestUrlList.some(url => url === event.request.url); - - const request = shouldServeIndexHtml ? 'index.html' : event.request; - const cache = await caches.open(cacheName); - cachedResponse = await cache.match(request); - } - - return cachedResponse || fetch(event.request); -} diff --git a/src/apps/blazor/infrastructure/Api/ApiClient.cs b/src/apps/blazor/infrastructure/Api/ApiClient.cs deleted file mode 100644 index 0de5930cbf..0000000000 --- a/src/apps/blazor/infrastructure/Api/ApiClient.cs +++ /dev/null @@ -1,6267 +0,0 @@ -//---------------------- -// -// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) -// -//---------------------- - -#nullable enable - -#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." -#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." -#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' -#pragma warning disable 612 // Disable "CS0612 '...' is obsolete" -#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null" -#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... -#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." -#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" -#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" -#pragma warning disable 8603 // Disable "CS8603 Possible null reference return" -#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" -#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type" -#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)." - -namespace FSH.Starter.Blazor.Infrastructure.Api -{ - using System = global::System; - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface IApiClient - { - /// - /// creates a brand - /// - /// - /// creates a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateBrandEndpointAsync(string version, CreateBrandCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a brand - /// - /// - /// creates a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateBrandEndpointAsync(string version, CreateBrandCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// gets brand by id - /// - /// - /// gets brand by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetBrandEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets brand by id - /// - /// - /// gets brand by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetBrandEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// update a brand - /// - /// - /// update a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateBrandEndpointAsync(string version, System.Guid id, UpdateBrandCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update a brand - /// - /// - /// update a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateBrandEndpointAsync(string version, System.Guid id, UpdateBrandCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// deletes brand by id - /// - /// - /// deletes brand by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteBrandEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// deletes brand by id - /// - /// - /// deletes brand by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteBrandEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// Gets a list of brands - /// - /// - /// Gets a list of brands with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SearchBrandsEndpointAsync(string version, SearchBrandsCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of brands - /// - /// - /// Gets a list of brands with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SearchBrandsEndpointAsync(string version, SearchBrandsCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// creates a product - /// - /// - /// creates a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateProductEndpointAsync(string version, CreateProductCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a product - /// - /// - /// creates a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateProductEndpointAsync(string version, CreateProductCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// gets product by id - /// - /// - /// gets prodct by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetProductEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets product by id - /// - /// - /// gets prodct by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetProductEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// update a product - /// - /// - /// update a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateProductEndpointAsync(string version, System.Guid id, UpdateProductCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update a product - /// - /// - /// update a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateProductEndpointAsync(string version, System.Guid id, UpdateProductCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// deletes product by id - /// - /// - /// deletes product by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteProductEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// deletes product by id - /// - /// - /// deletes product by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteProductEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// Gets a list of products - /// - /// - /// Gets a list of products with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SearchProductsEndpointAsync(string version, SearchProductsCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of products - /// - /// - /// Gets a list of products with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SearchProductsEndpointAsync(string version, SearchProductsCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Get role details by ID - /// - /// - /// Retrieve the details of a role by its ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetRoleByIdEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get role details by ID - /// - /// - /// Retrieve the details of a role by its ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetRoleByIdEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Delete a role by ID - /// - /// - /// Remove a role from the system by its ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DeleteRoleEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Delete a role by ID - /// - /// - /// Remove a role from the system by its ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DeleteRoleEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Get a list of all roles - /// - /// - /// Retrieve a list of all roles available in the system. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetRolesEndpointAsync(); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get a list of all roles - /// - /// - /// Retrieve a list of all roles available in the system. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetRolesEndpointAsync(System.Threading.CancellationToken cancellationToken); - - /// - /// Create or update a role - /// - /// - /// Create a new role or update an existing role. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateOrUpdateRoleEndpointAsync(CreateOrUpdateRoleCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Create or update a role - /// - /// - /// Create a new role or update an existing role. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateOrUpdateRoleEndpointAsync(CreateOrUpdateRoleCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// get role permissions - /// - /// - /// get role permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetRolePermissionsEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get role permissions - /// - /// - /// get role permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetRolePermissionsEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// update role permissions - /// - /// - /// update role permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateRolePermissionsEndpointAsync(string id, UpdatePermissionsCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update role permissions - /// - /// - /// update role permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateRolePermissionsEndpointAsync(string id, UpdatePermissionsCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// creates a tenant - /// - /// - /// creates a tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateTenantEndpointAsync(CreateTenantCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a tenant - /// - /// - /// creates a tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task CreateTenantEndpointAsync(CreateTenantCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// get tenants - /// - /// - /// get tenants - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetTenantsEndpointAsync(); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get tenants - /// - /// - /// get tenants - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetTenantsEndpointAsync(System.Threading.CancellationToken cancellationToken); - - /// - /// get tenant by id - /// - /// - /// get tenant by id - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTenantByIdEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get tenant by id - /// - /// - /// get tenant by id - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTenantByIdEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// upgrade tenant subscription - /// - /// - /// upgrade tenant subscription - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpgradeSubscriptionEndpointAsync(UpgradeSubscriptionCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// upgrade tenant subscription - /// - /// - /// upgrade tenant subscription - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpgradeSubscriptionEndpointAsync(UpgradeSubscriptionCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ActivateTenantEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ActivateTenantEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DisableTenantEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DisableTenantEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Creates a todo item - /// - /// - /// Creates a todo item - /// - /// The requested API version - /// Created - /// A server side error occurred. - System.Threading.Tasks.Task CreateTodoEndpointAsync(string version, CreateTodoCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Creates a todo item - /// - /// - /// Creates a todo item - /// - /// The requested API version - /// Created - /// A server side error occurred. - System.Threading.Tasks.Task CreateTodoEndpointAsync(string version, CreateTodoCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// gets todo item by id - /// - /// - /// gets todo item by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTodoEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets todo item by id - /// - /// - /// gets todo item by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTodoEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// Updates a todo item - /// - /// - /// Updated a todo item - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateTodoEndpointAsync(string version, System.Guid id, UpdateTodoCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Updates a todo item - /// - /// - /// Updated a todo item - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateTodoEndpointAsync(string version, System.Guid id, UpdateTodoCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Deletes a todo item - /// - /// - /// Deleted a todo item - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteTodoEndpointAsync(string version, System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Deletes a todo item - /// - /// - /// Deleted a todo item - /// - /// The requested API version - /// No Content - /// A server side error occurred. - System.Threading.Tasks.Task DeleteTodoEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken); - - /// - /// Gets a list of todo items with paging support - /// - /// - /// Gets a list of todo items with paging support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTodoListEndpointAsync(string version, PaginationFilter body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of todo items with paging support - /// - /// - /// Gets a list of todo items with paging support - /// - /// The requested API version - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetTodoListEndpointAsync(string version, PaginationFilter body, System.Threading.CancellationToken cancellationToken); - - /// - /// refresh JWTs - /// - /// - /// refresh JWTs - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task RefreshTokenEndpointAsync(string tenant, RefreshTokenCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// refresh JWTs - /// - /// - /// refresh JWTs - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task RefreshTokenEndpointAsync(string tenant, RefreshTokenCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// generate JWTs - /// - /// - /// generate JWTs - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task TokenGenerationEndpointAsync(string tenant, TokenGenerationCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// generate JWTs - /// - /// - /// generate JWTs - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task TokenGenerationEndpointAsync(string tenant, TokenGenerationCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// register user - /// - /// - /// register user - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task RegisterUserEndpointAsync(RegisterUserCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// register user - /// - /// - /// register user - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task RegisterUserEndpointAsync(RegisterUserCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// self register user - /// - /// - /// self register user - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SelfRegisterUserEndpointAsync(string tenant, RegisterUserCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// self register user - /// - /// - /// self register user - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task SelfRegisterUserEndpointAsync(string tenant, RegisterUserCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// update user profile - /// - /// - /// update user profile - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateUserEndpointAsync(UpdateUserCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update user profile - /// - /// - /// update user profile - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task UpdateUserEndpointAsync(UpdateUserCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Get current user information based on token - /// - /// - /// Get current user information based on token - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetMeEndpointAsync(); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get current user information based on token - /// - /// - /// Get current user information based on token - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetMeEndpointAsync(System.Threading.CancellationToken cancellationToken); - - /// - /// get users list - /// - /// - /// get users list - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUsersListEndpointAsync(); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get users list - /// - /// - /// get users list - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUsersListEndpointAsync(System.Threading.CancellationToken cancellationToken); - - /// - /// delete user profile - /// - /// - /// delete user profile - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DeleteUserEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// delete user profile - /// - /// - /// delete user profile - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task DeleteUserEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Get user profile by ID - /// - /// - /// Get another user's profile details by user ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetUserEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get user profile by ID - /// - /// - /// Get another user's profile details by user ID. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task GetUserEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Forgot password - /// - /// - /// Generates a password reset token and sends it via email. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ForgotPasswordEndpointAsync(string tenant, ForgotPasswordCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Forgot password - /// - /// - /// Generates a password reset token and sends it via email. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ForgotPasswordEndpointAsync(string tenant, ForgotPasswordCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Changes password - /// - /// - /// Change password - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ChangePasswordEndpointAsync(ChangePasswordCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Changes password - /// - /// - /// Change password - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ChangePasswordEndpointAsync(ChangePasswordCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Reset password - /// - /// - /// Resets the password using the token and new password provided. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ResetPasswordEndpointAsync(string tenant, ResetPasswordCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Reset password - /// - /// - /// Resets the password using the token and new password provided. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ResetPasswordEndpointAsync(string tenant, ResetPasswordCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// Get current user permissions - /// - /// - /// Get current user permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserPermissionsAsync(); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get current user permissions - /// - /// - /// Get current user permissions - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserPermissionsAsync(System.Threading.CancellationToken cancellationToken); - - /// - /// Toggle a user's active status - /// - /// - /// Toggle a user's active status - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ToggleUserStatusEndpointAsync(string id, ToggleUserStatusCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Toggle a user's active status - /// - /// - /// Toggle a user's active status - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task ToggleUserStatusEndpointAsync(string id, ToggleUserStatusCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// assign roles - /// - /// - /// assign roles - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task AssignRolesToUserEndpointAsync(string id, AssignUserRoleCommand body); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// assign roles - /// - /// - /// assign roles - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task AssignRolesToUserEndpointAsync(string id, AssignUserRoleCommand body, System.Threading.CancellationToken cancellationToken); - - /// - /// get user roles - /// - /// - /// get user roles - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserRolesEndpointAsync(string id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get user roles - /// - /// - /// get user roles - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserRolesEndpointAsync(string id, System.Threading.CancellationToken cancellationToken); - - /// - /// Get user's audit trail details - /// - /// - /// Get user's audit trail details. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserAuditTrailEndpointAsync(System.Guid id); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get user's audit trail details - /// - /// - /// Get user's audit trail details. - /// - /// OK - /// A server side error occurred. - System.Threading.Tasks.Task> GetUserAuditTrailEndpointAsync(System.Guid id, System.Threading.CancellationToken cancellationToken); - - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ApiClient : IApiClient - { - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private System.Text.Json.JsonSerializerOptions _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public ApiClient(System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - _httpClient = httpClient; - Initialize(); - } - - private static System.Text.Json.JsonSerializerOptions CreateSerializerSettings() - { - var settings = new System.Text.Json.JsonSerializerOptions(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - /// - /// creates a brand - /// - /// - /// creates a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateBrandEndpointAsync(string version, CreateBrandCommand body) - { - return CreateBrandEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a brand - /// - /// - /// creates a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateBrandEndpointAsync(string version, CreateBrandCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/brands" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/brands"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// gets brand by id - /// - /// - /// gets brand by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetBrandEndpointAsync(string version, System.Guid id) - { - return GetBrandEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets brand by id - /// - /// - /// gets brand by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetBrandEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/brands/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/brands/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// update a brand - /// - /// - /// update a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateBrandEndpointAsync(string version, System.Guid id, UpdateBrandCommand body) - { - return UpdateBrandEndpointAsync(version, id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update a brand - /// - /// - /// update a brand - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateBrandEndpointAsync(string version, System.Guid id, UpdateBrandCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/brands/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/brands/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// deletes brand by id - /// - /// - /// deletes brand by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DeleteBrandEndpointAsync(string version, System.Guid id) - { - return DeleteBrandEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// deletes brand by id - /// - /// - /// deletes brand by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DeleteBrandEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/brands/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/brands/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Gets a list of brands - /// - /// - /// Gets a list of brands with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task SearchBrandsEndpointAsync(string version, SearchBrandsCommand body) - { - return SearchBrandsEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of brands - /// - /// - /// Gets a list of brands with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task SearchBrandsEndpointAsync(string version, SearchBrandsCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/brands/search" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/brands/search"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// creates a product - /// - /// - /// creates a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateProductEndpointAsync(string version, CreateProductCommand body) - { - return CreateProductEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a product - /// - /// - /// creates a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateProductEndpointAsync(string version, CreateProductCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/products" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/products"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// gets product by id - /// - /// - /// gets prodct by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetProductEndpointAsync(string version, System.Guid id) - { - return GetProductEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets product by id - /// - /// - /// gets prodct by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetProductEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/products/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/products/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// update a product - /// - /// - /// update a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateProductEndpointAsync(string version, System.Guid id, UpdateProductCommand body) - { - return UpdateProductEndpointAsync(version, id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update a product - /// - /// - /// update a product - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateProductEndpointAsync(string version, System.Guid id, UpdateProductCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/products/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/products/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// deletes product by id - /// - /// - /// deletes product by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DeleteProductEndpointAsync(string version, System.Guid id) - { - return DeleteProductEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// deletes product by id - /// - /// - /// deletes product by id - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DeleteProductEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/products/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/products/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Gets a list of products - /// - /// - /// Gets a list of products with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task SearchProductsEndpointAsync(string version, SearchProductsCommand body) - { - return SearchProductsEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of products - /// - /// - /// Gets a list of products with pagination and filtering support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task SearchProductsEndpointAsync(string version, SearchProductsCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/catalog/products/search" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/catalog/products/search"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get role details by ID - /// - /// - /// Retrieve the details of a role by its ID. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetRoleByIdEndpointAsync(string id) - { - return GetRoleByIdEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get role details by ID - /// - /// - /// Retrieve the details of a role by its ID. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetRoleByIdEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles/{id}" - urlBuilder_.Append("api/roles/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Delete a role by ID - /// - /// - /// Remove a role from the system by its ID. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DeleteRoleEndpointAsync(string id) - { - return DeleteRoleEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Delete a role by ID - /// - /// - /// Remove a role from the system by its ID. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DeleteRoleEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles/{id}" - urlBuilder_.Append("api/roles/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get a list of all roles - /// - /// - /// Retrieve a list of all roles available in the system. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetRolesEndpointAsync() - { - return GetRolesEndpointAsync(System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get a list of all roles - /// - /// - /// Retrieve a list of all roles available in the system. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetRolesEndpointAsync(System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles" - urlBuilder_.Append("api/roles"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Create or update a role - /// - /// - /// Create a new role or update an existing role. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateOrUpdateRoleEndpointAsync(CreateOrUpdateRoleCommand body) - { - return CreateOrUpdateRoleEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Create or update a role - /// - /// - /// Create a new role or update an existing role. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateOrUpdateRoleEndpointAsync(CreateOrUpdateRoleCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles" - urlBuilder_.Append("api/roles"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// get role permissions - /// - /// - /// get role permissions - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetRolePermissionsEndpointAsync(string id) - { - return GetRolePermissionsEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get role permissions - /// - /// - /// get role permissions - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetRolePermissionsEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles/{id}/permissions" - urlBuilder_.Append("api/roles/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/permissions"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// update role permissions - /// - /// - /// update role permissions - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateRolePermissionsEndpointAsync(string id, UpdatePermissionsCommand body) - { - return UpdateRolePermissionsEndpointAsync(id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update role permissions - /// - /// - /// update role permissions - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateRolePermissionsEndpointAsync(string id, UpdatePermissionsCommand body, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/roles/{id}/permissions" - urlBuilder_.Append("api/roles/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/permissions"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// creates a tenant - /// - /// - /// creates a tenant - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateTenantEndpointAsync(CreateTenantCommand body) - { - return CreateTenantEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// creates a tenant - /// - /// - /// creates a tenant - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateTenantEndpointAsync(CreateTenantCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants" - urlBuilder_.Append("api/tenants"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// get tenants - /// - /// - /// get tenants - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetTenantsEndpointAsync() - { - return GetTenantsEndpointAsync(System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get tenants - /// - /// - /// get tenants - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetTenantsEndpointAsync(System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants" - urlBuilder_.Append("api/tenants"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// get tenant by id - /// - /// - /// get tenant by id - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTenantByIdEndpointAsync(string id) - { - return GetTenantByIdEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get tenant by id - /// - /// - /// get tenant by id - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTenantByIdEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants/{id}" - urlBuilder_.Append("api/tenants/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// upgrade tenant subscription - /// - /// - /// upgrade tenant subscription - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpgradeSubscriptionEndpointAsync(UpgradeSubscriptionCommand body) - { - return UpgradeSubscriptionEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// upgrade tenant subscription - /// - /// - /// upgrade tenant subscription - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpgradeSubscriptionEndpointAsync(UpgradeSubscriptionCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants/upgrade" - urlBuilder_.Append("api/tenants/upgrade"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task ActivateTenantEndpointAsync(string id) - { - return ActivateTenantEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ActivateTenantEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants/{id}/activate" - urlBuilder_.Append("api/tenants/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/activate"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DisableTenantEndpointAsync(string id) - { - return DisableTenantEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// activate tenant - /// - /// - /// activate tenant - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DisableTenantEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/tenants/{id}/deactivate" - urlBuilder_.Append("api/tenants/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/deactivate"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Creates a todo item - /// - /// - /// Creates a todo item - /// - /// The requested API version - /// Created - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateTodoEndpointAsync(string version, CreateTodoCommand body) - { - return CreateTodoEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Creates a todo item - /// - /// - /// Creates a todo item - /// - /// The requested API version - /// Created - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateTodoEndpointAsync(string version, CreateTodoCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/todos" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/todos"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 201) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// gets todo item by id - /// - /// - /// gets todo item by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTodoEndpointAsync(string version, System.Guid id) - { - return GetTodoEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// gets todo item by id - /// - /// - /// gets todo item by id - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTodoEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/todos/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/todos/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Updates a todo item - /// - /// - /// Updated a todo item - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateTodoEndpointAsync(string version, System.Guid id, UpdateTodoCommand body) - { - return UpdateTodoEndpointAsync(version, id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Updates a todo item - /// - /// - /// Updated a todo item - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateTodoEndpointAsync(string version, System.Guid id, UpdateTodoCommand body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/todos/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/todos/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Deletes a todo item - /// - /// - /// Deleted a todo item - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DeleteTodoEndpointAsync(string version, System.Guid id) - { - return DeleteTodoEndpointAsync(version, id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Deletes a todo item - /// - /// - /// Deleted a todo item - /// - /// The requested API version - /// No Content - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DeleteTodoEndpointAsync(string version, System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/todos/{id}" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/todos/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Gets a list of todo items with paging support - /// - /// - /// Gets a list of todo items with paging support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTodoListEndpointAsync(string version, PaginationFilter body) - { - return GetTodoListEndpointAsync(version, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets a list of todo items with paging support - /// - /// - /// Gets a list of todo items with paging support - /// - /// The requested API version - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTodoListEndpointAsync(string version, PaginationFilter body, System.Threading.CancellationToken cancellationToken) - { - if (version == null) - throw new System.ArgumentNullException("version"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/v{version}/todos/search" - urlBuilder_.Append("api/v"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(version, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/todos/search"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// refresh JWTs - /// - /// - /// refresh JWTs - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task RefreshTokenEndpointAsync(string tenant, RefreshTokenCommand body) - { - return RefreshTokenEndpointAsync(tenant, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// refresh JWTs - /// - /// - /// refresh JWTs - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RefreshTokenEndpointAsync(string tenant, RefreshTokenCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (tenant == null) - throw new System.ArgumentNullException("tenant"); - request_.Headers.TryAddWithoutValidation("tenant", ConvertToString(tenant, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/token/refresh" - urlBuilder_.Append("api/token/refresh"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// generate JWTs - /// - /// - /// generate JWTs - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task TokenGenerationEndpointAsync(string tenant, TokenGenerationCommand body) - { - return TokenGenerationEndpointAsync(tenant, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// generate JWTs - /// - /// - /// generate JWTs - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task TokenGenerationEndpointAsync(string tenant, TokenGenerationCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (tenant == null) - throw new System.ArgumentNullException("tenant"); - request_.Headers.TryAddWithoutValidation("tenant", ConvertToString(tenant, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/token" - urlBuilder_.Append("api/token"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// register user - /// - /// - /// register user - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task RegisterUserEndpointAsync(RegisterUserCommand body) - { - return RegisterUserEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// register user - /// - /// - /// register user - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RegisterUserEndpointAsync(RegisterUserCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/register" - urlBuilder_.Append("api/users/register"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// self register user - /// - /// - /// self register user - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task SelfRegisterUserEndpointAsync(string tenant, RegisterUserCommand body) - { - return SelfRegisterUserEndpointAsync(tenant, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// self register user - /// - /// - /// self register user - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task SelfRegisterUserEndpointAsync(string tenant, RegisterUserCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (tenant == null) - throw new System.ArgumentNullException("tenant"); - request_.Headers.TryAddWithoutValidation("tenant", ConvertToString(tenant, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/self-register" - urlBuilder_.Append("api/users/self-register"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// update user profile - /// - /// - /// update user profile - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateUserEndpointAsync(UpdateUserCommand body) - { - return UpdateUserEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// update user profile - /// - /// - /// update user profile - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateUserEndpointAsync(UpdateUserCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/profile" - urlBuilder_.Append("api/users/profile"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get current user information based on token - /// - /// - /// Get current user information based on token - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetMeEndpointAsync() - { - return GetMeEndpointAsync(System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get current user information based on token - /// - /// - /// Get current user information based on token - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetMeEndpointAsync(System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/profile" - urlBuilder_.Append("api/users/profile"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// get users list - /// - /// - /// get users list - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetUsersListEndpointAsync() - { - return GetUsersListEndpointAsync(System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get users list - /// - /// - /// get users list - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetUsersListEndpointAsync(System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users" - urlBuilder_.Append("api/users"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// delete user profile - /// - /// - /// delete user profile - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task DeleteUserEndpointAsync(string id) - { - return DeleteUserEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// delete user profile - /// - /// - /// delete user profile - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task DeleteUserEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get user profile by ID - /// - /// - /// Get another user's profile details by user ID. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetUserEndpointAsync(string id) - { - return GetUserEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get user profile by ID - /// - /// - /// Get another user's profile details by user ID. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetUserEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Forgot password - /// - /// - /// Generates a password reset token and sends it via email. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task ForgotPasswordEndpointAsync(string tenant, ForgotPasswordCommand body) - { - return ForgotPasswordEndpointAsync(tenant, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Forgot password - /// - /// - /// Generates a password reset token and sends it via email. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ForgotPasswordEndpointAsync(string tenant, ForgotPasswordCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (tenant == null) - throw new System.ArgumentNullException("tenant"); - request_.Headers.TryAddWithoutValidation("tenant", ConvertToString(tenant, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/forgot-password" - urlBuilder_.Append("api/users/forgot-password"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Changes password - /// - /// - /// Change password - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task ChangePasswordEndpointAsync(ChangePasswordCommand body) - { - return ChangePasswordEndpointAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Changes password - /// - /// - /// Change password - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ChangePasswordEndpointAsync(ChangePasswordCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/change-password" - urlBuilder_.Append("api/users/change-password"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Reset password - /// - /// - /// Resets the password using the token and new password provided. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task ResetPasswordEndpointAsync(string tenant, ResetPasswordCommand body) - { - return ResetPasswordEndpointAsync(tenant, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Reset password - /// - /// - /// Resets the password using the token and new password provided. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ResetPasswordEndpointAsync(string tenant, ResetPasswordCommand body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (tenant == null) - throw new System.ArgumentNullException("tenant"); - request_.Headers.TryAddWithoutValidation("tenant", ConvertToString(tenant, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/reset-password" - urlBuilder_.Append("api/users/reset-password"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get current user permissions - /// - /// - /// Get current user permissions - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetUserPermissionsAsync() - { - return GetUserPermissionsAsync(System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get current user permissions - /// - /// - /// Get current user permissions - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetUserPermissionsAsync(System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/permissions" - urlBuilder_.Append("api/users/permissions"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Toggle a user's active status - /// - /// - /// Toggle a user's active status - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task ToggleUserStatusEndpointAsync(string id, ToggleUserStatusCommand body) - { - return ToggleUserStatusEndpointAsync(id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Toggle a user's active status - /// - /// - /// Toggle a user's active status - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task ToggleUserStatusEndpointAsync(string id, ToggleUserStatusCommand body, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}/toggle-status" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/toggle-status"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// assign roles - /// - /// - /// assign roles - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task AssignRolesToUserEndpointAsync(string id, AssignUserRoleCommand body) - { - return AssignRolesToUserEndpointAsync(id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// assign roles - /// - /// - /// assign roles - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task AssignRolesToUserEndpointAsync(string id, AssignUserRoleCommand body, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}/roles" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/roles"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// get user roles - /// - /// - /// get user roles - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetUserRolesEndpointAsync(string id) - { - return GetUserRolesEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// get user roles - /// - /// - /// get user roles - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetUserRolesEndpointAsync(string id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}/roles" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/roles"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Get user's audit trail details - /// - /// - /// Get user's audit trail details. - /// - /// OK - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetUserAuditTrailEndpointAsync(System.Guid id) - { - return GetUserAuditTrailEndpointAsync(id, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Get user's audit trail details - /// - /// - /// Get user's audit trail details. - /// - /// OK - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetUserAuditTrailEndpointAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "api/users/{id}/audit-trails" - urlBuilder_.Append("api/users/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/audit-trails"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T)!, string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody!, responseText); - } - catch (System.Text.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - { - var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); - return new ObjectResponseResult(typedBody!, string.Empty); - } - } - catch (System.Text.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ActivateTenantResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("status")] - public string? Status { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class AssignUserRoleCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("userRoles")] - public System.Collections.Generic.ICollection? UserRoles { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class AuditTrail - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("userId")] - public System.Guid UserId { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("operation")] - public string? Operation { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("entity")] - public string? Entity { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("dateTime")] - public System.DateTime DateTime { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("previousValues")] - public string? PreviousValues { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("newValues")] - public string? NewValues { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("modifiedProperties")] - public string? ModifiedProperties { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("primaryKey")] - public string? PrimaryKey { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class BrandResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class BrandResponsePagedList - { - - [System.Text.Json.Serialization.JsonPropertyName("items")] - public System.Collections.Generic.ICollection? Items { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalCount")] - public int TotalCount { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalPages")] - public int TotalPages { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasPrevious")] - public bool HasPrevious { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasNext")] - public bool HasNext { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ChangePasswordCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("password")] - public string? Password { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("newPassword")] - public string? NewPassword { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("confirmNewPassword")] - public string? ConfirmNewPassword { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateBrandCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = "Sample Brand"; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = "Descriptive Description"; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateBrandResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateOrUpdateRoleCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateProductCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = "Sample Product"; - - [System.Text.Json.Serialization.JsonPropertyName("price")] - public double Price { get; set; } = 10D; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = "Descriptive Description"; - - [System.Text.Json.Serialization.JsonPropertyName("brandId")] - public System.Guid? BrandId { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateProductResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateTenantCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("connectionString")] - public string? ConnectionString { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("adminEmail")] - public string? AdminEmail { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("issuer")] - public string? Issuer { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateTenantResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateTodoCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("title")] - public string? Title { get; set; } = "Hello World!"; - - [System.Text.Json.Serialization.JsonPropertyName("note")] - public string? Note { get; set; } = "Important Note."; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateTodoResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class DisableTenantResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("status")] - public string? Status { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class FileUploadCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("extension")] - public string? Extension { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("data")] - public string? Data { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Filter - { - - [System.Text.Json.Serialization.JsonPropertyName("logic")] - public string? Logic { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("filters")] - public System.Collections.Generic.ICollection? Filters { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("field")] - public string? Field { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("operator")] - public string? Operator { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("value")] - public object? Value { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ForgotPasswordCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class GetTodoResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("title")] - public string? Title { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("note")] - public string? Note { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class PaginationFilter - { - - [System.Text.Json.Serialization.JsonPropertyName("advancedSearch")] - public Search AdvancedSearch { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("keyword")] - public string? Keyword { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("advancedFilter")] - public Filter AdvancedFilter { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("orderBy")] - public System.Collections.Generic.ICollection? OrderBy { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ProductResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("price")] - public double Price { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("brand")] - public BrandResponse Brand { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ProductResponsePagedList - { - - [System.Text.Json.Serialization.JsonPropertyName("items")] - public System.Collections.Generic.ICollection? Items { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalCount")] - public int TotalCount { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalPages")] - public int TotalPages { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasPrevious")] - public bool HasPrevious { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasNext")] - public bool HasNext { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class RefreshTokenCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("token")] - public string? Token { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("refreshToken")] - public string? RefreshToken { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class RegisterUserCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("firstName")] - public string? FirstName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("lastName")] - public string? LastName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("userName")] - public string? UserName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("password")] - public string? Password { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("confirmPassword")] - public string? ConfirmPassword { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("phoneNumber")] - public string? PhoneNumber { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class RegisterUserResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("userId")] - public string? UserId { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ResetPasswordCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("password")] - public string? Password { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("token")] - public string? Token { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class RoleDto - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("permissions")] - public System.Collections.Generic.ICollection? Permissions { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Search - { - - [System.Text.Json.Serialization.JsonPropertyName("fields")] - public System.Collections.Generic.ICollection? Fields { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("keyword")] - public string? Keyword { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class SearchBrandsCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("advancedSearch")] - public Search AdvancedSearch { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("keyword")] - public string? Keyword { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("advancedFilter")] - public Filter AdvancedFilter { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("orderBy")] - public System.Collections.Generic.ICollection? OrderBy { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class SearchProductsCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("advancedSearch")] - public Search AdvancedSearch { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("keyword")] - public string? Keyword { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("advancedFilter")] - public Filter AdvancedFilter { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("orderBy")] - public System.Collections.Generic.ICollection? OrderBy { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("brandId")] - public System.Guid? BrandId { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("minimumRate")] - public double? MinimumRate { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("maximumRate")] - public double? MaximumRate { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TenantDetail - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("connectionString")] - public string? ConnectionString { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("adminEmail")] - public string? AdminEmail { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("isActive")] - public bool IsActive { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("validUpto")] - public System.DateTime ValidUpto { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("issuer")] - public string? Issuer { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TodoDto - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("title")] - public string? Title { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("note")] - public string? Note { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TodoDtoPagedList - { - - [System.Text.Json.Serialization.JsonPropertyName("items")] - public System.Collections.Generic.ICollection? Items { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageNumber")] - public int PageNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("pageSize")] - public int PageSize { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalCount")] - public int TotalCount { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("totalPages")] - public int TotalPages { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasPrevious")] - public bool HasPrevious { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("hasNext")] - public bool HasNext { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ToggleUserStatusCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("activateUser")] - public bool ActivateUser { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("userId")] - public string? UserId { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TokenGenerationCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = "admin@root.com"; - - [System.Text.Json.Serialization.JsonPropertyName("password")] - public string? Password { get; set; } = "123Pa$$word!"; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TokenResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("token")] - public string? Token { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("refreshToken")] - public string? RefreshToken { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("refreshTokenExpiryTime")] - public System.DateTime RefreshTokenExpiryTime { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateBrandCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateBrandResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdatePermissionsCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("roleId")] - public string? RoleId { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("permissions")] - public System.Collections.Generic.ICollection? Permissions { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateProductCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("name")] - public string? Name { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("price")] - public double Price { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("brandId")] - public System.Guid? BrandId { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateProductResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateTodoCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("title")] - public string? Title { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("note")] - public string? Note { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateTodoResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid? Id { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateUserCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public string? Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("firstName")] - public string? FirstName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("lastName")] - public string? LastName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("phoneNumber")] - public string? PhoneNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("image")] - public FileUploadCommand Image { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("deleteCurrentImage")] - public bool DeleteCurrentImage { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpgradeSubscriptionCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("tenant")] - public string? Tenant { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("extendedExpiryDate")] - public System.DateTime ExtendedExpiryDate { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpgradeSubscriptionResponse - { - - [System.Text.Json.Serialization.JsonPropertyName("newValidity")] - public System.DateTime NewValidity { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("tenant")] - public string? Tenant { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UserDetail - { - - [System.Text.Json.Serialization.JsonPropertyName("id")] - public System.Guid Id { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("userName")] - public string? UserName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("firstName")] - public string? FirstName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("lastName")] - public string? LastName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("email")] - public string? Email { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("isActive")] - public bool IsActive { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("emailConfirmed")] - public bool EmailConfirmed { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("phoneNumber")] - public string? PhoneNumber { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("imageUrl")] - public System.Uri? ImageUrl { get; set; } = default!; - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UserRoleDetail - { - - [System.Text.Json.Serialization.JsonPropertyName("roleId")] - public string? RoleId { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("roleName")] - public string? RoleName { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("description")] - public string? Description { get; set; } = default!; - - [System.Text.Json.Serialization.JsonPropertyName("enabled")] - public bool Enabled { get; set; } = default!; - - } - - - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string? Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception? innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception? innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} - -#pragma warning restore 108 -#pragma warning restore 114 -#pragma warning restore 472 -#pragma warning restore 612 -#pragma warning restore 1573 -#pragma warning restore 1591 -#pragma warning restore 8073 -#pragma warning restore 3016 -#pragma warning restore 8603 -#pragma warning restore 8604 -#pragma warning restore 8625 \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Api/nswag.json b/src/apps/blazor/infrastructure/Api/nswag.json deleted file mode 100644 index 4d3fb1c43c..0000000000 --- a/src/apps/blazor/infrastructure/Api/nswag.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "runtime": "Net80", - "defaultVariables": null, - "documentGenerator": { - "fromDocument": { - "json": "", - "url": "https://localhost:7000/swagger/v1/swagger.json", - "output": null, - "newLineBehavior": "Auto" - } - }, - "codeGenerators": { - "openApiToCSharpClient": { - "clientBaseClass": null, - "configurationClass": null, - "generateClientClasses": true, - "generateClientInterfaces": true, - "injectHttpClient": true, - "disposeHttpClient": false, - "protectedMethods": [], - "generateExceptionClasses": true, - "exceptionClass": "ApiException", - "wrapDtoExceptions": true, - "useHttpClientCreationMethod": false, - "httpClientType": "System.Net.Http.HttpClient", - "useHttpRequestMessageCreationMethod": false, - "useBaseUrl": false, - "generateBaseUrlProperty": true, - "generateSyncMethods": false, - "generatePrepareRequestAndProcessResponseAsAsyncMethods": false, - "exposeJsonSerializerSettings": false, - "clientClassAccessModifier": "public", - "typeAccessModifier": "public", - "generateContractsOutput": false, - "contractsNamespace": null, - "contractsOutputFilePath": null, - "parameterDateTimeFormat": "s", - "parameterDateFormat": "yyyy-MM-dd", - "generateUpdateJsonSerializerSettingsMethod": true, - "useRequestAndResponseSerializationSettings": false, - "serializeTypeInformation": false, - "queryNullValue": "", - "className": "ApiClient", - "operationGenerationMode": "MultipleClientsFromOperationId", - "additionalNamespaceUsages": [], - "additionalContractNamespaceUsages": [], - "generateOptionalParameters": false, - "generateJsonMethods": false, - "enforceFlagEnums": false, - "parameterArrayType": "System.Collections.Generic.IEnumerable", - "parameterDictionaryType": "System.Collections.Generic.IDictionary", - "responseArrayType": "System.Collections.Generic.ICollection", - "responseDictionaryType": "System.Collections.Generic.IDictionary", - "wrapResponses": false, - "wrapResponseMethods": [], - "generateResponseClasses": true, - "responseClass": "SwaggerResponse", - "namespace": "FSH.Starter.Blazor.Infrastructure.Api", - "requiredPropertiesMustBeDefined": true, - "dateType": "System.DateTimeOffset", - "jsonConverters": null, - "anyType": "object", - "dateTimeType": "System.DateTime", - "timeType": "System.TimeSpan", - "timeSpanType": "System.TimeSpan", - "arrayType": "System.Collections.Generic.ICollection", - "arrayInstanceType": "System.Collections.ObjectModel.Collection", - "dictionaryType": "System.Collections.Generic.IDictionary", - "dictionaryInstanceType": "System.Collections.Generic.Dictionary", - "arrayBaseType": "System.Collections.ObjectModel.Collection", - "dictionaryBaseType": "System.Collections.Generic.Dictionary", - "classStyle": "Poco", - "jsonLibrary": "SystemTextJson", - "generateDefaultValues": true, - "generateDataAnnotations": true, - "excludedTypeNames": [], - "excludedParameterNames": [], - "handleReferences": false, - "generateImmutableArrayProperties": false, - "generateImmutableDictionaryProperties": false, - "jsonSerializerSettingsTransformationMethod": null, - "inlineNamedArrays": false, - "inlineNamedDictionaries": false, - "inlineNamedTuples": true, - "inlineNamedAny": false, - "generateDtoTypes": true, - "generateOptionalPropertiesAsNullable": false, - "generateNullableReferenceTypes": true, - "templateDirectory": null, - "typeNameGeneratorType": null, - "propertyNameGeneratorType": null, - "enumNameGeneratorType": null, - "serviceHost": null, - "serviceSchemes": null, - "output": "ApiClient.cs", - "newLineBehavior": "Auto" - } - } -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Auth/AuthorizationServiceExtensions.cs b/src/apps/blazor/infrastructure/Auth/AuthorizationServiceExtensions.cs deleted file mode 100644 index df265c8a4b..0000000000 --- a/src/apps/blazor/infrastructure/Auth/AuthorizationServiceExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using System.Security.Claims; - -namespace FSH.Starter.Blazor.Infrastructure.Auth; - -public static class AuthorizationServiceExtensions -{ - public static async Task HasPermissionAsync(this IAuthorizationService service, ClaimsPrincipal user, string action, string resource) - { - return (await service.AuthorizeAsync(user, null, FshPermission.NameFor(action, resource))).Succeeded; - } -} diff --git a/src/apps/blazor/infrastructure/Auth/Extensions.cs b/src/apps/blazor/infrastructure/Auth/Extensions.cs deleted file mode 100644 index 730ef26e7a..0000000000 --- a/src/apps/blazor/infrastructure/Auth/Extensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Auth.Jwt; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.Blazor.Infrastructure.Auth; -public static class Extensions -{ - public static IServiceCollection AddAuthentication(this IServiceCollection services, IConfiguration config) - { - services.AddScoped() - .AddScoped(sp => (IAuthenticationService)sp.GetRequiredService()) - .AddScoped(sp => (IAccessTokenProvider)sp.GetRequiredService()) - .AddScoped() - .AddScoped(); - - services.AddAuthorizationCore(RegisterPermissionClaims); - services.AddCascadingAuthenticationState(); - return services; - } - - - private static void RegisterPermissionClaims(AuthorizationOptions options) - { - foreach (var permission in FshPermissions.All.Select(p => p.Name)) - { - options.AddPolicy(permission, policy => policy.RequireClaim(FshClaims.Permission, permission)); - } - } -} diff --git a/src/apps/blazor/infrastructure/Auth/IAuthenticationService.cs b/src/apps/blazor/infrastructure/Auth/IAuthenticationService.cs deleted file mode 100644 index 4de354af27..0000000000 --- a/src/apps/blazor/infrastructure/Auth/IAuthenticationService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Api; - -namespace FSH.Starter.Blazor.Infrastructure.Auth; - -public interface IAuthenticationService -{ - - void NavigateToExternalLogin(string returnUrl); - - Task LoginAsync(string tenantId, TokenGenerationCommand request); - - Task LogoutAsync(); - - Task ReLoginAsync(string returnUrl); -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderAccessor.cs b/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderAccessor.cs deleted file mode 100644 index 52df7087ae..0000000000 --- a/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderAccessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.Blazor.Infrastructure.Auth.Jwt; - -public class AccessTokenProviderAccessor : IAccessTokenProviderAccessor -{ - private readonly IServiceProvider _provider; - private IAccessTokenProvider? _tokenProvider; - - public AccessTokenProviderAccessor(IServiceProvider provider) => - _provider = provider; - - public IAccessTokenProvider TokenProvider => - _tokenProvider ??= _provider.GetRequiredService(); -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderExtensions.cs b/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderExtensions.cs deleted file mode 100644 index 7004cfe5e4..0000000000 --- a/src/apps/blazor/infrastructure/Auth/Jwt/AccessTokenProviderExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; - -namespace FSH.Starter.Blazor.Infrastructure.Auth.Jwt; - -public static class AccessTokenProviderExtensions -{ - public static async Task GetAccessTokenAsync(this IAccessTokenProvider tokenProvider) => - (await tokenProvider.RequestAccessToken()) - .TryGetToken(out var token) - ? token.Value - : null; -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationHeaderHandler.cs b/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationHeaderHandler.cs deleted file mode 100644 index 6d9ad7656d..0000000000 --- a/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationHeaderHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; -using System.Net.Http.Headers; - -namespace FSH.Starter.Blazor.Infrastructure.Auth.Jwt; -public class JwtAuthenticationHeaderHandler : DelegatingHandler -{ - private readonly IAccessTokenProviderAccessor _tokenProviderAccessor; - private readonly NavigationManager _navigation; - - public JwtAuthenticationHeaderHandler(IAccessTokenProviderAccessor tokenProviderAccessor, NavigationManager navigation) - { - _tokenProviderAccessor = tokenProviderAccessor; - _navigation = navigation; - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - // skip token endpoints - if (request.RequestUri?.AbsolutePath.Contains("/token") is not true) - { - if (await _tokenProviderAccessor.TokenProvider.GetAccessTokenAsync() is string token) - { - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - } - else - { - _navigation.NavigateTo("/login"); - } - } - - return await base.SendAsync(request, cancellationToken); - } -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationService.cs b/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationService.cs deleted file mode 100644 index f129e35020..0000000000 --- a/src/apps/blazor/infrastructure/Auth/Jwt/JwtAuthenticationService.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System.Security.Claims; -using System.Text.Json; -using Blazored.LocalStorage; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Storage; -using FSH.Starter.Shared.Authorization; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; - -namespace FSH.Starter.Blazor.Infrastructure.Auth.Jwt; - -// This is a client-side AuthenticationStateProvider that determines the user's authentication state by -// looking for data persisted in the page when it was rendered on the server. This authentication state will -// be fixed for the lifetime of the WebAssembly application. So, if the user needs to log in or out, a full -// page reload is required. -// -// This only provides a user name and email for display purposes. It does not actually include any tokens -// that authenticate to the server when making subsequent requests. That works separately using a -// cookie that will be included on HttpClient requests to the server. -public sealed class JwtAuthenticationService : AuthenticationStateProvider, IAuthenticationService, IAccessTokenProvider -{ - private readonly SemaphoreSlim _semaphore = new(1, 1); - private readonly IApiClient _client; - private readonly ILocalStorageService _localStorage; - private readonly NavigationManager _navigation; - - public JwtAuthenticationService(PersistentComponentState state, ILocalStorageService localStorage, IApiClient client, NavigationManager navigation) - { - _localStorage = localStorage; - _client = client; - _navigation = navigation; - } - - public override async Task GetAuthenticationStateAsync() - { - string? cachedToken = await GetCachedAuthTokenAsync(); - if (string.IsNullOrWhiteSpace(cachedToken)) - { - return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); - } - - // Generate claimsIdentity from cached token - var claimsIdentity = new ClaimsIdentity(GetClaimsFromJwt(cachedToken), "jwt"); - - // Add cached permissions as claims - if (await GetCachedPermissionsAsync() is List cachedPermissions) - { - claimsIdentity.AddClaims(cachedPermissions.Select(p => new Claim(FshClaims.Permission, p))); - } - - return new AuthenticationState(new ClaimsPrincipal(claimsIdentity)); - } - - public async Task LoginAsync(string tenantId, TokenGenerationCommand request) - { - var tokenResponse = await _client.TokenGenerationEndpointAsync(tenantId, request); - - string? token = tokenResponse.Token; - string? refreshToken = tokenResponse.RefreshToken; - - if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(refreshToken)) - { - return false; - } - - await CacheAuthTokens(token, refreshToken); - - // Get permissions for the current user and add them to the cache - var permissions = await _client.GetUserPermissionsAsync(); - await CachePermissions(permissions); - - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - - return true; - } - - public async Task LogoutAsync() - { - await ClearCacheAsync(); - - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - - _navigation.NavigateTo("/login"); - } - - public void NavigateToExternalLogin(string returnUrl) - { - throw new NotImplementedException(); - } - - public async Task ReLoginAsync(string returnUrl) - { - await LogoutAsync(); - _navigation.NavigateTo(returnUrl); - } - - public async ValueTask RequestAccessToken(AccessTokenRequestOptions options) - { - return await RequestAccessToken(); - - } - - public async ValueTask RequestAccessToken() - { - // We make sure the access token is only refreshed by one thread at a time. The other ones have to wait. - await _semaphore.WaitAsync(); - try - { - var authState = await GetAuthenticationStateAsync(); - if (authState.User.Identity?.IsAuthenticated is not true) - { - return new AccessTokenResult(AccessTokenResultStatus.RequiresRedirect, new(), "/login", default); - } - - string? token = await GetCachedAuthTokenAsync(); - - //// Check if token needs to be refreshed (when its expiration time is less than 1 minute away) - var expTime = authState.User.GetExpiration(); - var diff = expTime - DateTime.UtcNow; - if (diff.TotalMinutes <= 1) - { - //return new AccessTokenResult(AccessTokenResultStatus.RequiresRedirect, new(), "/login", default); - string? refreshToken = await GetCachedRefreshTokenAsync(); - (bool succeeded, var response) = await TryRefreshTokenAsync(new RefreshTokenCommand { Token = token, RefreshToken = refreshToken }); - if (!succeeded) - { - return new AccessTokenResult(AccessTokenResultStatus.RequiresRedirect, new(), "/login", default); - } - - token = response?.Token; - } - - return new AccessTokenResult(AccessTokenResultStatus.Success, new AccessToken() { Value = token! }, string.Empty, default); - } - finally - { - _semaphore.Release(); - } - } - - private async Task<(bool Succeeded, TokenResponse? Token)> TryRefreshTokenAsync(RefreshTokenCommand request) - { - var authState = await GetAuthenticationStateAsync(); - string? tenantKey = authState.User.GetTenant(); - if (string.IsNullOrWhiteSpace(tenantKey)) - { - throw new InvalidOperationException("Can't refresh token when user is not logged in!"); - } - - try - { - var tokenResponse = await _client.RefreshTokenEndpointAsync(tenantKey, request); - - await CacheAuthTokens(tokenResponse.Token, tokenResponse.RefreshToken); - - return (true, tokenResponse); - } - catch - { - return (false, null); - } - } - - private async ValueTask CacheAuthTokens(string? token, string? refreshToken) - { - await _localStorage.SetItemAsync(StorageConstants.Local.AuthToken, token); - await _localStorage.SetItemAsync(StorageConstants.Local.RefreshToken, refreshToken); - } - - private ValueTask CachePermissions(ICollection permissions) - { - return _localStorage.SetItemAsync(StorageConstants.Local.Permissions, permissions); - } - - private async Task ClearCacheAsync() - { - await _localStorage.RemoveItemAsync(StorageConstants.Local.AuthToken); - await _localStorage.RemoveItemAsync(StorageConstants.Local.RefreshToken); - await _localStorage.RemoveItemAsync(StorageConstants.Local.Permissions); - } - private ValueTask GetCachedAuthTokenAsync() - { - return _localStorage.GetItemAsync(StorageConstants.Local.AuthToken); - } - - private ValueTask GetCachedRefreshTokenAsync() - { - return _localStorage.GetItemAsync(StorageConstants.Local.RefreshToken); - } - - private ValueTask?> GetCachedPermissionsAsync() - { - return _localStorage.GetItemAsync>(StorageConstants.Local.Permissions); - } - - private IEnumerable GetClaimsFromJwt(string jwt) - { - var claims = new List(); - string payload = jwt.Split('.')[1]; - byte[] jsonBytes = ParseBase64WithoutPadding(payload); - var keyValuePairs = JsonSerializer.Deserialize>(jsonBytes); - - if (keyValuePairs is not null) - { - keyValuePairs.TryGetValue(ClaimTypes.Role, out object? roles); - - if (roles is not null) - { - string? rolesString = roles.ToString(); - if (!string.IsNullOrEmpty(rolesString)) - { - if (rolesString.Trim().StartsWith("[")) - { - string[]? parsedRoles = JsonSerializer.Deserialize(rolesString); - - if (parsedRoles is not null) - { - claims.AddRange(parsedRoles.Select(role => new Claim(ClaimTypes.Role, role))); - } - } - else - { - claims.Add(new Claim(ClaimTypes.Role, rolesString)); - } - } - - keyValuePairs.Remove(ClaimTypes.Role); - } - - claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString() ?? string.Empty))); - } - - return claims; - } - private byte[] ParseBase64WithoutPadding(string payload) - { - payload = payload.Trim().Replace('-', '+').Replace('_', '/'); - string base64 = payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '='); - return Convert.FromBase64String(base64); - } -} diff --git a/src/apps/blazor/infrastructure/Auth/UserInfo.cs b/src/apps/blazor/infrastructure/Auth/UserInfo.cs deleted file mode 100644 index 28bdcff7e7..0000000000 --- a/src/apps/blazor/infrastructure/Auth/UserInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Security.Claims; - -namespace FSH.Starter.Blazor.Infrastructure.Auth; - -// Add properties to this class and update the server and client AuthenticationStateProviders -// to expose more information about the authenticated user to the client. -public sealed class UserInfo -{ - public required string UserId { get; init; } - public required string Name { get; init; } - - public const string UserIdClaimType = "sub"; - public const string NameClaimType = "name"; - - public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) => - new() - { - UserId = GetRequiredClaim(principal, UserIdClaimType), - Name = GetRequiredClaim(principal, NameClaimType), - }; - - public ClaimsPrincipal ToClaimsPrincipal() => - new(new ClaimsIdentity( - [new(UserIdClaimType, UserId), new(NameClaimType, Name)], - authenticationType: nameof(UserInfo), - nameType: NameClaimType, - roleType: null)); - - private static string GetRequiredClaim(ClaimsPrincipal principal, string claimType) => - principal.FindFirst(claimType)?.Value ?? throw new InvalidOperationException($"Could not find required '{claimType}' claim."); -} diff --git a/src/apps/blazor/infrastructure/Directory.Packages.props b/src/apps/blazor/infrastructure/Directory.Packages.props deleted file mode 100644 index 22802bc43d..0000000000 --- a/src/apps/blazor/infrastructure/Directory.Packages.props +++ /dev/null @@ -1,22 +0,0 @@ - - - true - true - true - - - true - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Extensions.cs b/src/apps/blazor/infrastructure/Extensions.cs deleted file mode 100644 index 10adc054a0..0000000000 --- a/src/apps/blazor/infrastructure/Extensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Globalization; -using Blazored.LocalStorage; -using FSH.Starter.Blazor.Infrastructure.Api; -using FSH.Starter.Blazor.Infrastructure.Auth; -using FSH.Starter.Blazor.Infrastructure.Auth.Jwt; -using FSH.Starter.Blazor.Infrastructure.Notifications; -using FSH.Starter.Blazor.Infrastructure.Preferences; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using MudBlazor; -using MudBlazor.Services; - -namespace FSH.Starter.Blazor.Infrastructure; -public static class Extensions -{ - private const string ClientName = "FullStackHero.API"; - public static IServiceCollection AddClientServices(this IServiceCollection services, IConfiguration config) - { - services.AddMudServices(configuration => - { - configuration.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight; - configuration.SnackbarConfiguration.HideTransitionDuration = 100; - configuration.SnackbarConfiguration.ShowTransitionDuration = 100; - configuration.SnackbarConfiguration.VisibleStateDuration = 3000; - configuration.SnackbarConfiguration.ShowCloseIcon = false; - }); - services.AddBlazoredLocalStorage(); - services.AddAuthentication(config); - services.AddTransient(); - services.AddHttpClient(ClientName, client => - { - client.DefaultRequestHeaders.AcceptLanguage.Clear(); - client.DefaultRequestHeaders.AcceptLanguage.ParseAdd(CultureInfo.DefaultThreadCurrentCulture?.TwoLetterISOLanguageName); - client.BaseAddress = new Uri(config["ApiBaseUrl"]!); - }) - .AddHttpMessageHandler() - .Services - .AddScoped(sp => sp.GetRequiredService().CreateClient(ClientName)); - services.AddTransient(); - services.AddTransient(); - services.AddNotifications(); - return services; - - } -} diff --git a/src/apps/blazor/infrastructure/Infrastructure.csproj b/src/apps/blazor/infrastructure/Infrastructure.csproj deleted file mode 100644 index d53d6be854..0000000000 --- a/src/apps/blazor/infrastructure/Infrastructure.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net9.0 - enable - enable - FSH.Starter.Blazor.Infrastructure - FSH.Starter.Blazor.Infrastructure - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/apps/blazor/infrastructure/Notifications/ConnectionState.cs b/src/apps/blazor/infrastructure/Notifications/ConnectionState.cs deleted file mode 100644 index 69cf9bb2d3..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/ConnectionState.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Starter.Blazor.Infrastructure.Notifications; - -public enum ConnectionState -{ - Connected, - Connecting, - Disconnected -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Notifications/ConnectionStateChanged.cs b/src/apps/blazor/infrastructure/Notifications/ConnectionStateChanged.cs deleted file mode 100644 index ae23346385..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/ConnectionStateChanged.cs +++ /dev/null @@ -1,5 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; - -namespace FSH.Starter.Blazor.Infrastructure.Notifications; - -public record ConnectionStateChanged(ConnectionState State, string? Message) : INotificationMessage; \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Notifications/Extensions.cs b/src/apps/blazor/infrastructure/Notifications/Extensions.cs deleted file mode 100644 index 872b6b8a7a..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/Extensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; -using MediatR; -using MediatR.Courier; -using Microsoft.Extensions.DependencyInjection; - -namespace FSH.Starter.Blazor.Infrastructure.Notifications; -internal static class Extensions -{ - public static IServiceCollection AddNotifications(this IServiceCollection services) - { - // Add mediator processing of notifications - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - services - .AddMediatR(cfg => - { - cfg.RegisterServicesFromAssemblies(assemblies); - }) - .AddCourier(assemblies) - .AddTransient(); - - // Register handlers for all INotificationMessages - foreach (var eventType in assemblies - .SelectMany(a => a.GetTypes()) - .Where(t => t.GetInterfaces().Any(i => i == typeof(INotificationMessage)))) - { - services.AddSingleton( - typeof(INotificationHandler<>).MakeGenericType( - typeof(NotificationWrapper<>).MakeGenericType(eventType)), - serviceProvider => serviceProvider.GetRequiredService(typeof(MediatRCourier))); - } - - return services; - } -} diff --git a/src/apps/blazor/infrastructure/Notifications/INotificationPublisher.cs b/src/apps/blazor/infrastructure/Notifications/INotificationPublisher.cs deleted file mode 100644 index 8ebca593be..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/INotificationPublisher.cs +++ /dev/null @@ -1,8 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; - -namespace FSH.Starter.Blazor.Infrastructure.Notifications; - -public interface INotificationPublisher -{ - Task PublishAsync(INotificationMessage notification); -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Notifications/NotificationPublisher.cs b/src/apps/blazor/infrastructure/Notifications/NotificationPublisher.cs deleted file mode 100644 index 7bab4ecfc4..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/NotificationPublisher.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace FSH.Starter.Blazor.Infrastructure.Notifications; - -public class NotificationPublisher : INotificationPublisher -{ - private readonly ILogger _logger; - private readonly IPublisher _mediator; - - public NotificationPublisher(ILogger logger, IPublisher mediator) => - (_logger, _mediator) = (logger, mediator); - - public Task PublishAsync(INotificationMessage notification) - { - _logger.LogInformation("Publishing Notification : {notification}", notification.GetType().Name); - return _mediator.Publish(CreateNotificationWrapper(notification)); - } - - private INotification CreateNotificationWrapper(INotificationMessage notification) => - (INotification)Activator.CreateInstance( - typeof(NotificationWrapper<>).MakeGenericType(notification.GetType()), notification)!; -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Notifications/NotificationWrapper.cs b/src/apps/blazor/infrastructure/Notifications/NotificationWrapper.cs deleted file mode 100644 index adf1aba2cc..0000000000 --- a/src/apps/blazor/infrastructure/Notifications/NotificationWrapper.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; -using MediatR; - -namespace FSH.Starter.Blazor.Infrastructure.Notifications; - -public class NotificationWrapper : INotification - where TNotificationMessage : INotificationMessage -{ - public NotificationWrapper(TNotificationMessage notification) => Notification = notification; - - public TNotificationMessage Notification { get; } -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Preferences/ClientPreference.cs b/src/apps/blazor/infrastructure/Preferences/ClientPreference.cs deleted file mode 100644 index 0003635571..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/ClientPreference.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FSH.Starter.Blazor.Infrastructure.Themes; - -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public class ClientPreference : IPreference -{ - public bool IsDarkMode { get; set; } = true; - public bool IsRTL { get; set; } - public bool IsDrawerOpen { get; set; } - public string PrimaryColor { get; set; } = CustomColors.Light.Primary; - public string SecondaryColor { get; set; } = CustomColors.Light.Secondary; - public double BorderRadius { get; set; } = 5; - public FshTablePreference TablePreference { get; set; } = new FshTablePreference(); -} diff --git a/src/apps/blazor/infrastructure/Preferences/ClientPreferenceManager.cs b/src/apps/blazor/infrastructure/Preferences/ClientPreferenceManager.cs deleted file mode 100644 index bd11448a53..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/ClientPreferenceManager.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System.Text.RegularExpressions; -using Blazored.LocalStorage; -using FSH.Starter.Blazor.Infrastructure.Themes; -using MudBlazor; - -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public class ClientPreferenceManager : IClientPreferenceManager -{ - private readonly ILocalStorageService _localStorageService; - - public ClientPreferenceManager( - ILocalStorageService localStorageService) - { - _localStorageService = localStorageService; - } - - public async Task ToggleDarkModeAsync() - { - if (await GetPreference() is ClientPreference preference) - { - preference.IsDarkMode = !preference.IsDarkMode; - await SetPreference(preference); - return !preference.IsDarkMode; - } - - return false; - } - - public async Task ToggleDrawerAsync() - { - if (await GetPreference() is ClientPreference preference) - { - preference.IsDrawerOpen = !preference.IsDrawerOpen; - await SetPreference(preference); - return preference.IsDrawerOpen; - } - - return false; - } - - public async Task ToggleLayoutDirectionAsync() - { - if (await GetPreference() is ClientPreference preference) - { - preference.IsRTL = !preference.IsRTL; - await SetPreference(preference); - return preference.IsRTL; - } - - return false; - } - - public async Task ChangeLanguageAsync(string languageCode) - { - //if (await GetPreference() is ClientPreference preference) - //{ - // var language = Array.Find(LocalizationConstants.SupportedLanguages, a => a.Code == languageCode); - // if (language?.Code is not null) - // { - // preference.LanguageCode = language.Code; - // preference.IsRTL = language.IsRTL; - // } - // else - // { - // preference.LanguageCode = "en-EN"; - // preference.IsRTL = false; - // } - - // await SetPreference(preference); - // return true; - //} - - return false; - } - - public async Task GetCurrentThemeAsync() - { - if (await GetPreference() is ClientPreference preference && preference.IsDarkMode) - return new FshTheme(); - - return new FshTheme(); - } - - public async Task GetPrimaryColorAsync() - { - if (await GetPreference() is ClientPreference preference) - { - string colorCode = preference.PrimaryColor; - if (Regex.Match(colorCode, "^#(?:[0-9a-fA-F]{3,4}){1,2}$").Success) - { - return colorCode; - } - else - { - preference.PrimaryColor = CustomColors.Light.Primary; - await SetPreference(preference); - return preference.PrimaryColor; - } - } - - return CustomColors.Light.Primary; - } - - public async Task IsRTL() - { - if (await GetPreference() is ClientPreference preference) - { - return preference.IsRTL; - } - - return false; - } - - public async Task IsDrawerOpen() - { - if (await GetPreference() is ClientPreference preference) - { - return preference.IsDrawerOpen; - } - - return false; - } - - public static string Preference = "clientPreference"; - - public async Task GetPreference() - { - return await _localStorageService.GetItemAsync(Preference) ?? new ClientPreference(); - } - - public async Task SetPreference(IPreference preference) - { - await _localStorageService.SetItemAsync(Preference, preference as ClientPreference); - } -} diff --git a/src/apps/blazor/infrastructure/Preferences/FshTablePreference.cs b/src/apps/blazor/infrastructure/Preferences/FshTablePreference.cs deleted file mode 100644 index f5595061bd..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/FshTablePreference.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FSH.Starter.Blazor.Shared.Notifications; - -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public class FshTablePreference : INotificationMessage -{ - public bool IsDense { get; set; } - public bool IsStriped { get; set; } - public bool HasBorder { get; set; } - public bool IsHoverable { get; set; } -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Preferences/IClientPreferenceManager.cs b/src/apps/blazor/infrastructure/Preferences/IClientPreferenceManager.cs deleted file mode 100644 index 444fd15c3f..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/IClientPreferenceManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MudBlazor; - -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public interface IClientPreferenceManager : IPreferenceManager -{ - Task GetCurrentThemeAsync(); - - Task ToggleDarkModeAsync(); - - Task ToggleDrawerAsync(); - - Task ToggleLayoutDirectionAsync(); -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Preferences/IPreference.cs b/src/apps/blazor/infrastructure/Preferences/IPreference.cs deleted file mode 100644 index 8909a3ad48..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/IPreference.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public interface IPreference -{ - -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Preferences/IPreferenceManager.cs b/src/apps/blazor/infrastructure/Preferences/IPreferenceManager.cs deleted file mode 100644 index 041f44603b..0000000000 --- a/src/apps/blazor/infrastructure/Preferences/IPreferenceManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FSH.Starter.Blazor.Infrastructure.Preferences; - -public interface IPreferenceManager -{ - Task SetPreference(IPreference preference); - - Task GetPreference(); - - Task ChangeLanguageAsync(string languageCode); -} \ No newline at end of file diff --git a/src/apps/blazor/infrastructure/Storage/StorageConstants.cs b/src/apps/blazor/infrastructure/Storage/StorageConstants.cs deleted file mode 100644 index 120cf4c5e5..0000000000 --- a/src/apps/blazor/infrastructure/Storage/StorageConstants.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FSH.Starter.Blazor.Infrastructure.Storage; -public static class StorageConstants -{ - public static class Local - { - public static string Preference = "clientPreference"; - - public static string AuthToken = "authToken"; - public static string RefreshToken = "refreshToken"; - public static string ImageUri = "userImageURL"; - public static string Permissions = "permissions"; - } -} diff --git a/src/apps/blazor/infrastructure/Themes/CustomColors.cs b/src/apps/blazor/infrastructure/Themes/CustomColors.cs deleted file mode 100644 index 5d72078418..0000000000 --- a/src/apps/blazor/infrastructure/Themes/CustomColors.cs +++ /dev/null @@ -1,42 +0,0 @@ -using MudBlazor; - -namespace FSH.Starter.Blazor.Infrastructure.Themes; - -public static class CustomColors -{ - public static readonly List ThemeColors = new() - { - Light.Primary, - Colors.Blue.Default, - Colors.Purple.Default, - Colors.Orange.Default, - Colors.Red.Default, - Colors.Amber.Default, - Colors.DeepPurple.Default, - Colors.Pink.Default, - Colors.Indigo.Default, - Colors.LightBlue.Default, - Colors.Cyan.Default, - Colors.Green.Default, - }; - - public static class Light - { - public const string Primary = "rgba(76,175,80,1)"; - public const string Secondary = "rgba(33,150,243,1)"; - public const string Background = "#FFF"; - public const string AppbarBackground = "#FFF"; - public const string AppbarText = "#6e6e6e"; - } - - public static class Dark - { - public const string Primary = "rgba(76,175,80,1)"; - public const string Secondary = "rgba(33,150,243,1)"; - public const string Background = "#1b1f22"; - public const string AppbarBackground = "#1b1f22"; - public const string DrawerBackground = "#121212"; - public const string Surface = "#202528"; - public const string Disabled = "#545454"; - } -} diff --git a/src/apps/blazor/infrastructure/Themes/CustomTypography.cs b/src/apps/blazor/infrastructure/Themes/CustomTypography.cs deleted file mode 100644 index 69faf18ed2..0000000000 --- a/src/apps/blazor/infrastructure/Themes/CustomTypography.cs +++ /dev/null @@ -1,114 +0,0 @@ -using MudBlazor; - -namespace FSH.Starter.Blazor.Infrastructure.Themes; - -public static class CustomTypography -{ - public static Typography FshTypography => new Typography() - { - Default = new DefaultTypography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = ".875rem", - FontWeight = "400", - LineHeight = "1.43", - LetterSpacing = ".01071em" - }, - H1 = new H1Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "3rem", - FontWeight = "300", - LineHeight = "1.167", - LetterSpacing = "-.01562em" - }, - H2 = new H2Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "2.75rem", - FontWeight = "300", - LineHeight = "1.2", - LetterSpacing = "-.00833em" - }, - H3 = new H3Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "2rem", - FontWeight = "400", - LineHeight = "1.167", - LetterSpacing = "0" - }, - H4 = new H4Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "1.75rem", - FontWeight = "400", - LineHeight = "1.235", - LetterSpacing = ".00735em" - }, - H5 = new H5Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "1.5rem", - FontWeight = "400", - LineHeight = "1.334", - LetterSpacing = "0" - }, - H6 = new H6Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "1.25rem", - FontWeight = "400", - LineHeight = "1.6", - LetterSpacing = ".0075em" - }, - Button = new ButtonTypography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = ".875rem", - FontWeight = "400", - LineHeight = "1.75", - LetterSpacing = ".02857em" - }, - Body1 = new Body1Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "1rem", - FontWeight = "400", - LineHeight = "1.5", - LetterSpacing = ".00938em" - }, - Body2 = new Body2Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = ".875rem", - FontWeight = "400", - LineHeight = "1.43", - LetterSpacing = ".01071em" - }, - Caption = new CaptionTypography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = ".75rem", - FontWeight = "200", - LineHeight = "1.66", - LetterSpacing = ".03333em" - }, - Subtitle1 = new Subtitle1Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = "1rem", - FontWeight = "400", - LineHeight = "1.57", - LetterSpacing = ".00714em" - }, - Subtitle2 = new Subtitle2Typography() - { - FontFamily = ["Montserrat", "Helvetica", "Arial", "sans-serif"], - FontSize = ".875rem", - FontWeight = "400", - LineHeight = "1.57", - LetterSpacing = ".00714em" - } - }; -} diff --git a/src/apps/blazor/infrastructure/Themes/FshTheme.cs b/src/apps/blazor/infrastructure/Themes/FshTheme.cs deleted file mode 100644 index 233ad52ebb..0000000000 --- a/src/apps/blazor/infrastructure/Themes/FshTheme.cs +++ /dev/null @@ -1,57 +0,0 @@ -using MudBlazor; - -namespace FSH.Starter.Blazor.Infrastructure.Themes; - -public class FshTheme : MudTheme -{ - public FshTheme() - { - PaletteLight = new PaletteLight() - { - Primary = CustomColors.Light.Primary, - Secondary = CustomColors.Light.Secondary, - Background = CustomColors.Light.Background, - AppbarBackground = CustomColors.Light.AppbarBackground, - AppbarText = CustomColors.Light.AppbarText, - DrawerBackground = CustomColors.Light.Background, - DrawerText = "rgba(0,0,0, 0.7)", - Success = CustomColors.Light.Primary, - TableLines = "#e0e0e029", - OverlayDark = "hsl(0deg 0% 0% / 75%)" - }; - - PaletteDark = new PaletteDark() - { - Primary = CustomColors.Dark.Primary, - Secondary = CustomColors.Dark.Secondary, - Success = CustomColors.Dark.Primary, - Black = "#27272f", - Background = CustomColors.Dark.Background, - Surface = CustomColors.Dark.Surface, - DrawerBackground = CustomColors.Dark.DrawerBackground, - DrawerText = "rgba(255,255,255, 0.50)", - AppbarBackground = CustomColors.Dark.AppbarBackground, - AppbarText = "rgba(255,255,255, 0.70)", - TextPrimary = "rgba(255,255,255, 0.70)", - TextSecondary = "rgba(255,255,255, 0.50)", - ActionDefault = "#adadb1", - ActionDisabled = "rgba(255,255,255, 0.26)", - ActionDisabledBackground = "rgba(255,255,255, 0.12)", - DrawerIcon = "rgba(255,255,255, 0.50)", - TableLines = "#e0e0e036", - Dark = CustomColors.Dark.DrawerBackground, - Divider = "#e0e0e036", - OverlayDark = "hsl(0deg 0% 0% / 75%)", - TextDisabled = CustomColors.Dark.Disabled - }; - - LayoutProperties = new LayoutProperties() - { - DefaultBorderRadius = "5px" - }; - - Typography = CustomTypography.FshTypography; - Shadows = new Shadow(); - ZIndex = new ZIndex() { Drawer = 1300 }; - } -} \ No newline at end of file diff --git a/src/apps/blazor/nginx.conf b/src/apps/blazor/nginx.conf deleted file mode 100644 index 9c2af758d5..0000000000 --- a/src/apps/blazor/nginx.conf +++ /dev/null @@ -1,19 +0,0 @@ -events { } - -http { - include mime.types; - - types { - application/wasm wasm; - } - - server { - listen 80; - index index.html; - - location / { - root /usr/share/nginx/html; - try_files $uri $uri/ /index.html =404; - } - } -} diff --git a/src/apps/blazor/scripts/nswag-regen.ps1 b/src/apps/blazor/scripts/nswag-regen.ps1 deleted file mode 100644 index 0d88d60bc5..0000000000 --- a/src/apps/blazor/scripts/nswag-regen.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -# This script is cross-platform, supporting all OSes that PowerShell Core/7 runs on. - -$currentDirectory = Get-Location -$rootDirectory = git rev-parse --show-toplevel -$hostDirectory = Join-Path -Path $rootDirectory -ChildPath 'src/apps/blazor/client' -$infrastructurePrj = Join-Path -Path $rootDirectory -ChildPath 'src/apps/blazor/infrastructure/Infrastructure.csproj' - -Write-Host "Make sure you have run the WebAPI project. `n" -Write-Host "Press any key to continue... `n" -$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); - -Set-Location -Path $hostDirectory -Write-Host "Host Directory is $hostDirectory `n" - -<# Run command #> -dotnet build -t:NSwag $infrastructurePrj - -Set-Location -Path $currentDirectory -Write-Host -NoNewLine 'NSwag Regenerated. Press any key to continue...'; -$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); diff --git a/src/apps/blazor/shared/Notifications/BasicNotification.cs b/src/apps/blazor/shared/Notifications/BasicNotification.cs deleted file mode 100644 index 6401c24f18..0000000000 --- a/src/apps/blazor/shared/Notifications/BasicNotification.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FSH.Starter.Blazor.Shared.Notifications; - -public class BasicNotification : INotificationMessage -{ - public enum LabelType - { - Information, - Success, - Warning, - Error - } - - public string? Message { get; set; } - public LabelType Label { get; set; } -} \ No newline at end of file diff --git a/src/apps/blazor/shared/Notifications/INotificationMessage.cs b/src/apps/blazor/shared/Notifications/INotificationMessage.cs deleted file mode 100644 index 48dd686d8e..0000000000 --- a/src/apps/blazor/shared/Notifications/INotificationMessage.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Starter.Blazor.Shared.Notifications; - -public interface INotificationMessage -{ -} \ No newline at end of file diff --git a/src/apps/blazor/shared/Notifications/JobNotification.cs b/src/apps/blazor/shared/Notifications/JobNotification.cs deleted file mode 100644 index 9b645a6d66..0000000000 --- a/src/apps/blazor/shared/Notifications/JobNotification.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FSH.Starter.Blazor.Shared.Notifications; - -public class JobNotification : INotificationMessage -{ - public string? Message { get; set; } - public string? JobId { get; set; } - public decimal Progress { get; set; } -} \ No newline at end of file diff --git a/src/apps/blazor/shared/Notifications/NotificationConstants.cs b/src/apps/blazor/shared/Notifications/NotificationConstants.cs deleted file mode 100644 index 69e0b764b3..0000000000 --- a/src/apps/blazor/shared/Notifications/NotificationConstants.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FSH.Starter.Blazor.Shared.Notifications; - -public static class NotificationConstants -{ - public const string NotificationFromServer = nameof(NotificationFromServer); -} \ No newline at end of file diff --git a/src/apps/blazor/shared/Notifications/StatsChangedNotification.cs b/src/apps/blazor/shared/Notifications/StatsChangedNotification.cs deleted file mode 100644 index 7b8f4f006f..0000000000 --- a/src/apps/blazor/shared/Notifications/StatsChangedNotification.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace FSH.Starter.Blazor.Shared.Notifications; - -public class StatsChangedNotification : INotificationMessage -{ -} \ No newline at end of file diff --git a/src/apps/blazor/shared/Shared.csproj b/src/apps/blazor/shared/Shared.csproj deleted file mode 100644 index 16aa766eec..0000000000 --- a/src/apps/blazor/shared/Shared.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net9.0 - enable - enable - FSH.Starter.Blazor.Shared - FSH.Starter.Blazor.Shared - - - diff --git a/src/aspire/Host/Host.csproj b/src/aspire/Host/Host.csproj deleted file mode 100644 index 35f6caac53..0000000000 --- a/src/aspire/Host/Host.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - FSH.Starter.Aspire - FSH.Starter.Aspire - Exe - net9.0 - enable - enable - true - a007d645-3346-446a-89ab-2bb3fdeebb54 - - - - - - - - - - - - - - - diff --git a/src/aspire/Host/Program.cs b/src/aspire/Host/Program.cs deleted file mode 100644 index 1875e66fa7..0000000000 --- a/src/aspire/Host/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -var builder = DistributedApplication.CreateBuilder(args); - -builder.AddContainer("grafana", "grafana/grafana") - .WithBindMount("../../../compose/grafana/config", "/etc/grafana", isReadOnly: true) - .WithBindMount("../../../compose/grafana/dashboards", "/var/lib/grafana/dashboards", isReadOnly: true) - .WithHttpEndpoint(port: 3000, targetPort: 3000, name: "http"); - -builder.AddContainer("prometheus", "prom/prometheus") - .WithBindMount("../../../compose/prometheus", "/etc/prometheus", isReadOnly: true) - .WithHttpEndpoint(port: 9090, targetPort: 9090); - -var username = builder.AddParameter("pg-username", "admin"); -var password = builder.AddParameter("pg-password", "admin"); - -var database = builder.AddPostgres("db", username, password, port: 5432) - .WithDataVolume() - .AddDatabase("fullstackhero"); - -var api = builder.AddProject("webapi") - .WaitFor(database); - -var blazor = builder.AddProject("blazor"); - -using var app = builder.Build(); - -await app.RunAsync(); diff --git a/src/aspire/Host/Properties/launchSettings.json b/src/aspire/Host/Properties/launchSettings.json deleted file mode 100644 index 946a56bbb7..0000000000 --- a/src/aspire/Host/Properties/launchSettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:7200;http://localhost:5200", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21192", - "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22197" - } - } - } -} diff --git a/src/aspire/Host/appsettings.Development.json b/src/aspire/Host/appsettings.Development.json deleted file mode 100644 index 0c208ae918..0000000000 --- a/src/aspire/Host/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/src/aspire/Host/appsettings.json b/src/aspire/Host/appsettings.json deleted file mode 100644 index 31c092aa45..0000000000 --- a/src/aspire/Host/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Aspire.Hosting.Dcp": "Warning" - } - } -} diff --git a/src/aspire/service-defaults/Extensions.cs b/src/aspire/service-defaults/Extensions.cs deleted file mode 100644 index 7893d5361b..0000000000 --- a/src/aspire/service-defaults/Extensions.cs +++ /dev/null @@ -1,152 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -namespace FSH.Starter.Aspire.ServiceDefaults; - -// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. -// This project should be referenced by each service project in your solution. -// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults -public static class Extensions -{ - public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) - { - builder.ConfigureOpenTelemetry(); - - builder.AddDefaultHealthChecks(); - - builder.Services.AddServiceDiscovery(); - - builder.Services.ConfigureHttpClientDefaults(http => - { - // Turn on resilience by default - http.AddStandardResilienceHandler(); - - // Turn on service discovery by default - http.AddServiceDiscovery(); - }); - - return builder; - } - - public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) - { - #region OpenTelemetry - - // Configure OpenTelemetry service resource details - // See https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions - var entryAssembly = System.Reflection.Assembly.GetEntryAssembly(); - var entryAssemblyName = entryAssembly?.GetName(); - var versionAttribute = entryAssembly?.GetCustomAttributes(false) - .OfType() - .FirstOrDefault(); - var resourceServiceName = entryAssemblyName?.Name; - var resourceServiceVersion = versionAttribute?.InformationalVersion ?? entryAssemblyName?.Version?.ToString(); - var attributes = new Dictionary - { - ["host.name"] = Environment.MachineName, - ["service.names"] = - "FSH.Starter.WebApi.Host", //builder.Configuration["OpenTelemetrySettings:ServiceName"]!, //It's a WA Fix because the service.name tag is not completed automatically by Resource.Builder()...AddService(serviceName) https://github.com/open-telemetry/opentelemetry-dotnet/issues/2027 - ["os.description"] = System.Runtime.InteropServices.RuntimeInformation.OSDescription, - ["deployment.environment"] = builder.Environment.EnvironmentName.ToLowerInvariant() - }; - var resourceBuilder = ResourceBuilder.CreateDefault() - .AddService(serviceName: resourceServiceName, serviceVersion: resourceServiceVersion) - .AddTelemetrySdk() - //.AddEnvironmentVariableDetector() - .AddAttributes(attributes); - - #endregion region - - - builder.Logging.AddOpenTelemetry(logging => - { - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; - logging.SetResourceBuilder(resourceBuilder); - }); - - builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics.SetResourceBuilder(resourceBuilder) - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation() - .AddProcessInstrumentation() - .AddMeter(MetricsConstants.Todos) - .AddMeter(MetricsConstants.Catalog); - }) - .WithTracing(tracing => - { - if (builder.Environment.IsDevelopment()) - { - tracing.SetSampler(new AlwaysOnSampler()); - } - - tracing.SetResourceBuilder(resourceBuilder) - .AddAspNetCoreInstrumentation(nci => nci.RecordException = true) - .AddHttpClientInstrumentation() - .AddEntityFrameworkCoreInstrumentation(); - }); - - builder.AddOpenTelemetryExporters(); - - return builder; - } - - private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) - { - var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); - - if (useOtlpExporter) - { - builder.Services.AddOpenTelemetry().UseOtlpExporter(); - } - - // The following lines enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) - builder.Services.AddOpenTelemetry() - // BUG: Part of the workaround for https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1617 - .WithMetrics(metrics => metrics.AddPrometheusExporter(options => - { - options.DisableTotalNameSuffixForCounters = true; - })); - - return builder; - } - - public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) - { - builder.Services.AddHealthChecks() - // Add a default liveness check to ensure app is responsive - .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); - - return builder; - } - - public static WebApplication MapDefaultEndpoints(this WebApplication app) - { - // The following line enables the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) - app.UseOpenTelemetryPrometheusScrapingEndpoint( - context => - { - if (context.Request.Path != "/metrics") return false; - return true; - }); - - // All health checks must pass for app to be considered ready to accept traffic after starting - app.MapHealthChecks("/health").AllowAnonymous(); - // Only health checks tagged with the "live" tag must pass for app to be considered alive - app.MapHealthChecks("/alive", new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") }) - .AllowAnonymous(); - - return app; - } -} diff --git a/src/aspire/service-defaults/MetricsConstants.cs b/src/aspire/service-defaults/MetricsConstants.cs deleted file mode 100644 index 57874dfe95..0000000000 --- a/src/aspire/service-defaults/MetricsConstants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FSH.Starter.Aspire.ServiceDefaults; -public static class MetricsConstants -{ - public const string AppName = "fullstackhero"; - public const string Todos = "Todos"; - public const string Catalog = "Catalog"; -} diff --git a/src/aspire/service-defaults/ServiceDefaults.csproj b/src/aspire/service-defaults/ServiceDefaults.csproj deleted file mode 100644 index 26553c1c7b..0000000000 --- a/src/aspire/service-defaults/ServiceDefaults.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - FSH.Starter.Aspire.ServiceDefaults - FSH.Starter.Aspire.ServiceDefaults - net9.0 - enable - enable - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/terraform/.gitignore b/terraform/.gitignore deleted file mode 100644 index 4776104d1e..0000000000 --- a/terraform/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -# Local .terraform directories -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Crash log files -crash.log -crash.*.log - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -# *.tfvars -# *.tfvars.json - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -# Ignore transient lock info files created by terraform apply -.terraform.tfstate.lock.info - -# Include override files you do wish to add to version control using negated pattern -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* - -# Ignore CLI configuration files -.terraformrc -terraform.rc -.terraform.lock.hcl \ No newline at end of file diff --git a/terraform/README.md b/terraform/README.md deleted file mode 100644 index 4ad56c1cb2..0000000000 --- a/terraform/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Deploying FullStackHero .NET Starter Kit to AWS with Terraform - -## Pre-Requisites - -1. Ensure you have the latest Terraform installed on your machine. I used `Terraform v1.8.4` at the time of development. -2. You should have installed AWS CLI Tools and authenticated your machine to access AWS. - -## Bootstrapping - -If you are deploying this for the first time, navigate to `./boostrap` and run the following commands - -```terraform -terraform init -terraform plan -terraform apply --auto-approve -``` - -This will provision the required backend resources on AWS that terraform would need in the next steps. Basics this will create an Amazon S3 Bucket, and DynamoDB Table. - -## Deploy - -Navigate to `./environments/dev/` and run the following. - -``` -terraform init -terraform plan -terraform apply --auto-approve -``` - -This will deploy the following, - -- ECS Cluster and Task (.NET Web API) -- RDS PostgreSQL Database Instance -- Required Networking Components like VPC, ALB etc. - -## Destroy - -Once you are done with your testing, ensure that you delete the resources to keep your bill under control. - -```` -terraform destroy --auto-approve -``` -```` diff --git a/terraform/bootstrap/database.tf b/terraform/bootstrap/database.tf deleted file mode 100644 index 05293f37a5..0000000000 --- a/terraform/bootstrap/database.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "aws_dynamodb_table" "dynamodb-terraform-states" { - name = "fullstackhero-state-locks" - hash_key = "LockID" - read_capacity = 20 - write_capacity = 20 - attribute { - name = "LockID" - type = "S" - } -} diff --git a/terraform/bootstrap/storage.tf b/terraform/bootstrap/storage.tf deleted file mode 100644 index a732bd2645..0000000000 --- a/terraform/bootstrap/storage.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "aws_s3_bucket" "s3_bucket" { - bucket = "fullstackhero-terraform-backend" - tags = { - Name = "fullstackhero-terraform-backend" - Project = "fullstackhero" - } - lifecycle { - prevent_destroy = true - } -} diff --git a/terraform/environments/dev/compute.tf b/terraform/environments/dev/compute.tf deleted file mode 100644 index 41201d2fea..0000000000 --- a/terraform/environments/dev/compute.tf +++ /dev/null @@ -1,45 +0,0 @@ -module "cluster" { - source = "../../modules/ecs/cluster" - cluster_name = "fullstackhero" -} - -module "webapi" { - source = "../../modules/ecs" - vpc_id = module.vpc.vpc_id - environment = var.environment - cluster_id = module.cluster.id - service_name = "webapi" - container_name = "fsh-webapi" - container_image = "ghcr.io/fullstackhero/webapi:latest" - subnet_ids = [module.vpc.private_a_id, module.vpc.private_b_id] - environment_variables = { - DatabaseOptions__ConnectionString = module.rds.connection_string - DatabaseOptions__Provider = "postgresql" - Serilog__MinimumLevel__Default = "Error" - CorsOptions__AllowedOrigins__0 = "http://${module.blazor.endpoint}" - OriginOptions__OriginUrl = "http://${module.webapi.endpoint}:8080" - } -} - -module "blazor" { - source = "../../modules/ecs" - vpc_id = module.vpc.vpc_id - cluster_id = module.cluster.id - environment = var.environment - container_port = 80 - service_name = "blazor" - container_name = "fsh-blazor" - container_image = "ghcr.io/fullstackhero/blazor:latest" - subnet_ids = [module.vpc.private_a_id, module.vpc.private_b_id] - environment_variables = { - Frontend_FSHStarterBlazorClient_Settings__AppSettingsTemplate = "/usr/share/nginx/html/appsettings.json.TEMPLATE" - Frontend_FSHStarterBlazorClient_Settings__AppSettingsJson = "/usr/share/nginx/html/appsettings.json" - FSHStarterBlazorClient_ApiBaseUrl = "http://${module.webapi.endpoint}:8080" - ApiBaseUrl = "http://${module.webapi.endpoint}:8080" - } - entry_point = [ - "/bin/sh", - "-c", - "envsubst < $${Frontend_FSHStarterBlazorClient_Settings__AppSettingsTemplate} > $${Frontend_FSHStarterBlazorClient_Settings__AppSettingsJson} || echo 'envsubst failed' && find /usr/share/nginx/html -type f | xargs chmod +r || echo 'chmod failed' && echo 'Entry point execution completed' && cat /usr/share/nginx/html/appsettings.json && exec nginx -g 'daemon off;'" - ] -} diff --git a/terraform/environments/dev/database.tf b/terraform/environments/dev/database.tf deleted file mode 100644 index 3547680027..0000000000 --- a/terraform/environments/dev/database.tf +++ /dev/null @@ -1,9 +0,0 @@ -module "rds" { - environment = var.environment - source = "../../modules/rds" - vpc_id = module.vpc.vpc_id - subnet_ids = [module.vpc.private_a_id, module.vpc.private_b_id] - multi_az = false - database_name = "fsh" - cidr_block = module.vpc.cidr_block -} diff --git a/terraform/environments/dev/main.tf b/terraform/environments/dev/main.tf deleted file mode 100644 index e789efddf1..0000000000 --- a/terraform/environments/dev/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - backend "s3" { - bucket = "fullstackhero-terraform-backend" - key = "fullstackhero/dev/terraform.tfstate" - region = "us-east-1" - dynamodb_table = "fullstackhero-state-locks" - } -} diff --git a/terraform/environments/dev/network.tf b/terraform/environments/dev/network.tf deleted file mode 100644 index 55e55df792..0000000000 --- a/terraform/environments/dev/network.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "vpc" { - source = "../../modules/vpc" -} diff --git a/terraform/environments/dev/outputs.tf b/terraform/environments/dev/outputs.tf deleted file mode 100644 index 3a04c8c615..0000000000 --- a/terraform/environments/dev/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "webapi" { - value = "http://${module.webapi.endpoint}:8080" -} - -output "blazor" { - value = "http://${module.blazor.endpoint}" -} diff --git a/terraform/environments/dev/providers.tf b/terraform/environments/dev/providers.tf deleted file mode 100644 index 8e8b661f74..0000000000 --- a/terraform/environments/dev/providers.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = "~> 1.8" - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } -} - -provider "aws" { - region = var.aws_region - default_tags { - tags = merge(local.common_tags) - } -} diff --git a/terraform/environments/dev/terraform.tfvars b/terraform/environments/dev/terraform.tfvars deleted file mode 100644 index 7ac85169db..0000000000 --- a/terraform/environments/dev/terraform.tfvars +++ /dev/null @@ -1,5 +0,0 @@ -// Default Project Tags -environment = "dev" -owner = "Mukesh Murugan" -project_name = "fullstackhero" -repository = "https://github.com/fullstackhero/dotnet-starter-kit" diff --git a/terraform/environments/dev/variables.tf b/terraform/environments/dev/variables.tf deleted file mode 100644 index 87662c7ee0..0000000000 --- a/terraform/environments/dev/variables.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "aws_region" { - description = "The AWS region to deploy resources in" - type = string - default = "us-east-1" -} - -variable "environment" { - type = string - default = "dev" -} -variable "owner" { - type = string -} - -variable "project_name" { - type = string -} - -variable "repository" { - type = string -} - - -locals { - common_tags = { - Environment = var.environment - Owner = var.owner - Project = var.project_name - Repository = var.repository - } -} diff --git a/terraform/modules/cloudwatch/main.tf b/terraform/modules/cloudwatch/main.tf deleted file mode 100644 index 3d571382ed..0000000000 --- a/terraform/modules/cloudwatch/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_cloudwatch_log_group" "this" { - name = var.log_group_name - retention_in_days = var.retention_period -} diff --git a/terraform/modules/cloudwatch/variables.tf b/terraform/modules/cloudwatch/variables.tf deleted file mode 100644 index e2eac862f0..0000000000 --- a/terraform/modules/cloudwatch/variables.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "log_group_name" { - type = string -} - -variable "retention_period" { - type = number - default = 60 -} diff --git a/terraform/modules/ecs/cluster/main.tf b/terraform/modules/ecs/cluster/main.tf deleted file mode 100644 index 236f30bd34..0000000000 --- a/terraform/modules/ecs/cluster/main.tf +++ /dev/null @@ -1,13 +0,0 @@ -resource "aws_ecs_cluster" "this" { - name = var.cluster_name -} - -resource "aws_ecs_cluster_capacity_providers" "this" { - cluster_name = aws_ecs_cluster.this.name - capacity_providers = ["FARGATE"] - default_capacity_provider_strategy { - base = 1 - weight = 100 - capacity_provider = "FARGATE" - } -} diff --git a/terraform/modules/ecs/cluster/outputs.tf b/terraform/modules/ecs/cluster/outputs.tf deleted file mode 100644 index af88b19431..0000000000 --- a/terraform/modules/ecs/cluster/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "id" { - value = aws_ecs_cluster.this.id -} diff --git a/terraform/modules/ecs/cluster/variables.tf b/terraform/modules/ecs/cluster/variables.tf deleted file mode 100644 index abbf86f798..0000000000 --- a/terraform/modules/ecs/cluster/variables.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "cluster_name" { - type = string -} diff --git a/terraform/modules/ecs/iam.tf b/terraform/modules/ecs/iam.tf deleted file mode 100644 index 0533ec0115..0000000000 --- a/terraform/modules/ecs/iam.tf +++ /dev/null @@ -1,41 +0,0 @@ -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.service_name}-ecs-ter" - assume_role_policy = <