├── .dockerignore ├── .editorconfig ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── dependabot.yml └── workflows │ ├── certificate-processing.api.yml │ ├── codeql-analysis.yml │ ├── enrolling.api.yml │ ├── eschool.graphql.yml │ ├── exam-management.api.yml │ ├── frontend.blazor.yml │ ├── library-management.api.yml │ ├── result-processing.api.yml │ ├── sanitycheck.yml │ └── webstatus.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build └── dotnet │ ├── Common.props │ ├── eSchool.ruleset │ └── stylecop.json ├── deploy └── k8s │ └── charts │ ├── README.md │ ├── enrolling │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ └── virtualservice.yaml │ └── values.yaml │ ├── eschool-gateway.yaml │ ├── mssql │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── destinationrule.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── peerauthentication.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml │ └── setup.bat ├── docker-compose-test.override.yml ├── docker-compose-test.yml ├── docker-compose.dcproj ├── docker-compose.override.yml ├── docker-compose.yml ├── eSchool.sln └── src ├── ApiGateways └── eSchool.GraphQL │ ├── Dockerfile │ ├── Enrollings │ ├── EnrollingQueries.cs │ └── EnrollmentMutations.cs │ ├── OpenAPIs │ └── Enrolling.json │ ├── Program.cs │ ├── RestApiErrorMiddleware.cs │ ├── Startup.cs │ ├── appsettings.json │ └── eSchool.GraphQL.csproj ├── Directory.Build.props ├── Libraries └── OpenTelemetry │ ├── IConfigurationOptions.cs │ ├── JaegerOptions.cs │ ├── OpenTelemetry.csproj │ ├── OpenTelemetryOptions.cs │ └── ServiceCollectionExtensions.cs ├── Services ├── CertificateProcessing │ └── CertificateProcessing.API │ │ ├── CertificateProcessing.API.csproj │ │ ├── Controllers │ │ └── HelloWorldController.cs │ │ ├── Dockerfile │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── Enrolling │ ├── Enrolling.API │ │ ├── Application │ │ │ ├── Behaviors │ │ │ │ └── LoggingBehavior.cs │ │ │ ├── Commands │ │ │ │ ├── EnrollmentApplicationCommand.cs │ │ │ │ └── EnrollmentApplicationCommandHandler.cs │ │ │ ├── Queries │ │ │ │ ├── FindAllEnrollmentsHandler.cs │ │ │ │ ├── FindAllEnrollmentsQuery.cs │ │ │ │ ├── GetEnrollmentByIdHandler.cs │ │ │ │ └── GetEnrollmentByIdQuery.cs │ │ │ └── Validations │ │ │ │ └── EnrollmentApplicationCommandValidator.cs │ │ ├── Controllers │ │ │ └── EnrollmentsController.cs │ │ ├── Dockerfile │ │ ├── Enrolling.API.csproj │ │ ├── Extensions │ │ │ └── ServiceCollectionExtensions.cs │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── WebHostExtensions.cs │ │ └── appsettings.json │ ├── Enrolling.Domain │ │ ├── AggregatesModel │ │ │ └── EnrollmentAggregate │ │ │ │ ├── Enrollment.cs │ │ │ │ ├── EnrollmentId.cs │ │ │ │ └── IEnrollmentRepository.cs │ │ ├── Enrolling.Domain.csproj │ │ └── SeedWork │ │ │ ├── Entity.cs │ │ │ ├── IAggregateRoot.cs │ │ │ └── IRepository.cs │ ├── Enrolling.FunctionalTests │ │ ├── Enrolling.FunctionalTests.csproj │ │ ├── EnrollingTests.cs │ │ ├── TestServerCollection.cs │ │ ├── TestServerFixture.cs │ │ └── appsettings.json │ ├── Enrolling.Infrastructure │ │ ├── Configuration │ │ │ └── ValueConverters │ │ │ │ ├── EnrollmentIdValueConverter.cs │ │ │ │ └── ModelConfigurationBuilderExtensions.cs │ │ ├── Enrolling.Infrastructure.csproj │ │ ├── EnrollingContext.cs │ │ ├── Migrations │ │ │ ├── 20200330154056_Initial.Designer.cs │ │ │ ├── 20200330154056_Initial.cs │ │ │ └── EnrollingContextModelSnapshot.cs │ │ └── Repositories │ │ │ └── EnrollmentRepository.cs │ └── Enrolling.UnitTests │ │ ├── Builders │ │ ├── EnrollmentDto.cs │ │ └── EnrollmentDtoBuilder.cs │ │ ├── Domain │ │ └── EnrollmentAggregateTests.cs │ │ └── Enrolling.UnitTests.csproj ├── ExamManagement │ └── ExamManagement.API │ │ ├── Controllers │ │ └── HelloWorldController.cs │ │ ├── Dockerfile │ │ ├── ExamManagement.API.csproj │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── LibraryManagement │ └── LibraryManagement.API │ │ ├── Controllers │ │ └── HelloWorldController.cs │ │ ├── Dockerfile │ │ ├── LibraryManagement.API.csproj │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json └── ResultProcessing │ └── ResultProcessing.API │ ├── Controllers │ └── HelloWorldController.cs │ ├── Dockerfile │ ├── Program.cs │ ├── ResultProcessing.API.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json └── Web ├── Frontend.Blazor ├── .config │ └── dotnet-tools.json ├── Frontend.Blazor.Client │ ├── .config │ │ └── dotnet-tools.json │ ├── .graphqlrc.json │ ├── App.razor │ ├── EnrollmentQuery.graphql │ ├── Frontend.Blazor.Client.csproj │ ├── Pages │ │ ├── Counter.razor │ │ ├── Enrollments │ │ │ ├── CreateEnrollment.razor │ │ │ └── Enrollments.razor │ │ └── Index.razor │ ├── Program.cs │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ └── NavMenu.razor │ ├── _Imports.razor │ ├── schema.extensions.graphql │ ├── schema.graphql │ └── wwwroot │ │ ├── favicon.ico │ │ ├── icon-512.png │ │ ├── index.html │ │ ├── manifest.json │ │ ├── service-worker.js │ │ └── service-worker.published.js ├── Frontend.Blazor.Server │ ├── Controllers │ │ └── AppSettingsController.cs │ ├── Dockerfile │ ├── Frontend.Blazor.Server.csproj │ ├── Pages │ │ ├── Error.cshtml │ │ └── Error.cshtml.cs │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── Frontend.Blazor.Shared │ ├── Frontend.Blazor.Shared.csproj │ └── FrontendSettings.cs └── README.md └── WebStatus ├── Dockerfile ├── Program.cs ├── Startup.cs ├── WebStatus.csproj ├── appsettings.Development.json └── appsettings.json /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.vs 6 | **/.vscode 7 | **/*.*proj.user 8 | **/bin 9 | **/obj 10 | **/Dockerfile 11 | **/docker-compose.yml 12 | **/docker-compose.*.yml 13 | **/*.dbmdl 14 | **/*.jfm 15 | **/.toolstarget -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence 3 | * @ratanparai @mahedee @iamkajal 4 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [contact@ratanparai.com](mailto:contact@ratanparai.com). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | day: "friday" 8 | time: "05:00" 9 | timezone: Asia/Dhaka 10 | open-pull-requests-limit: 10 11 | reviewers: 12 | - ratanparai 13 | labels: 14 | - "dependencies" 15 | -------------------------------------------------------------------------------- /.github/workflows/certificate-processing.api.yml: -------------------------------------------------------------------------------- 1 | name: CertificateProcessing.API 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Services/CertificateProcessing/**" 7 | - ".github/workflows/certificate-processing.api.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Services/CertificateProcessing/**" 19 | - ".github/workflows/certificate-processing.api.yml" 20 | - "src/Libraries/OpenTelemetry/**" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | build: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v1 35 | 36 | - name: set image tag 37 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 38 | 39 | - name: build 40 | run: docker-compose build certificateprocessing.api 41 | 42 | - uses: azure/docker-login@v1 43 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 44 | with: 45 | login-server: index.docker.io 46 | username: ${{ secrets.DOCKERIO_USERNAME }} 47 | password: ${{ secrets.DOCKERIO_PASSWORD }} 48 | 49 | - name: push image 50 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 51 | run: docker-compose push certificateprocessing.api 52 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '35 21 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'csharp', 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | - uses: actions/setup-dotnet@v1 41 | with: 42 | dotnet-version: '6.0.x' 43 | include-prerelease: true 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v1 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 54 | 55 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 56 | # If this step fails, then you should remove it and run the build manually (see below) 57 | - name: Autobuild 58 | uses: github/codeql-action/autobuild@v1 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 https://git.io/JvXDl 62 | 63 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 64 | # and modify them (or add more) to build your code if your project 65 | # uses a compiled language 66 | 67 | #- run: | 68 | # make bootstrap 69 | # make release 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v1 73 | -------------------------------------------------------------------------------- /.github/workflows/enrolling.api.yml: -------------------------------------------------------------------------------- 1 | name: Enrolling.API 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Services/Enrolling/**" 7 | - ".github/workflows/enrolling.api.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "eSchool.sln" 10 | - "build/dotnet/**" 11 | - "src/Directory.Build.props" 12 | - "docker-compose.yml" 13 | - "docker-compose.override.yml" 14 | - "docker-compose.dcproj" 15 | - "docker-compose-test.yml" 16 | - "docker-compose-test.override.yml" 17 | pull_request: 18 | paths: 19 | - "src/Services/Enrolling/**" 20 | - ".github/workflows/enrolling.api.yml" 21 | - "src/Libraries/OpenTelemetry/**" 22 | - "eSchool.sln" 23 | - "build/dotnet/**" 24 | - "src/Directory.Build.props" 25 | - "docker-compose.yml" 26 | - "docker-compose.override.yml" 27 | - "docker-compose.dcproj" 28 | - "docker-compose-test.yml" 29 | - "docker-compose-test.override.yml" 30 | 31 | jobs: 32 | build: 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - uses: actions/checkout@v1 37 | 38 | - name: set image tag 39 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 40 | 41 | - name: build 42 | run: docker-compose build enrolling.api 43 | 44 | - name: unit tests 45 | run: docker-compose -f docker-compose-test.yml -f docker-compose-test.override.yml run enrolling.api.test 46 | 47 | - name: functional tests 48 | run: docker-compose -f docker-compose-test.yml -f docker-compose-test.override.yml run enrolling.api.functional-test 49 | 50 | - uses: azure/docker-login@v1 51 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 52 | with: 53 | login-server: index.docker.io 54 | username: ${{ secrets.DOCKERIO_USERNAME }} 55 | password: ${{ secrets.DOCKERIO_PASSWORD }} 56 | 57 | - name: push image 58 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 59 | run: docker-compose push enrolling.api 60 | -------------------------------------------------------------------------------- /.github/workflows/eschool.graphql.yml: -------------------------------------------------------------------------------- 1 | name: ESchool.GraphQL 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/ApiGateways/eSchool.GraphQL/**" 7 | - ".github/workflows/eschool.graphql.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "eSchool.sln" 10 | - "build/dotnet/**" 11 | - "src/Directory.Build.props" 12 | - "docker-compose.yml" 13 | - "docker-compose.override.yml" 14 | - "docker-compose.dcproj" 15 | - "docker-compose-test.yml" 16 | - "docker-compose-test.override.yml" 17 | pull_request: 18 | paths: 19 | - "src/ApiGateways/eSchool.GraphQL/**" 20 | - ".github/workflows/eschool.graphql.yml" 21 | - "src/Libraries/OpenTelemetry/**" 22 | - "eSchool.sln" 23 | - "build/dotnet/**" 24 | - "src/Directory.Build.props" 25 | - "docker-compose.yml" 26 | - "docker-compose.override.yml" 27 | - "docker-compose.dcproj" 28 | - "docker-compose-test.yml" 29 | - "docker-compose-test.override.yml" 30 | 31 | jobs: 32 | build: 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - uses: actions/checkout@v1 37 | 38 | - name: set image tag 39 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 40 | 41 | - name: build 42 | run: docker-compose build eschool.graphql 43 | 44 | - uses: azure/docker-login@v1 45 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 46 | with: 47 | login-server: index.docker.io 48 | username: ${{ secrets.DOCKERIO_USERNAME }} 49 | password: ${{ secrets.DOCKERIO_PASSWORD }} 50 | 51 | - name: push image 52 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 53 | run: docker-compose push eschool.graphql 54 | -------------------------------------------------------------------------------- /.github/workflows/exam-management.api.yml: -------------------------------------------------------------------------------- 1 | name: ExamManagement.API 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Services/ExamManagement/**" 7 | - ".github/workflows/exam-management.api.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Services/ExamManagement/**" 19 | - ".github/workflows/exam-management.api.yml" 20 | - "src/Libraries/OpenTelemetry/**" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | build: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v1 35 | 36 | - name: set image tag 37 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 38 | 39 | - name: build 40 | run: docker-compose build exammanagement.api 41 | 42 | - uses: azure/docker-login@v1 43 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 44 | with: 45 | login-server: index.docker.io 46 | username: ${{ secrets.DOCKERIO_USERNAME }} 47 | password: ${{ secrets.DOCKERIO_PASSWORD }} 48 | 49 | - name: push image 50 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 51 | run: docker-compose push exammanagement.api 52 | -------------------------------------------------------------------------------- /.github/workflows/frontend.blazor.yml: -------------------------------------------------------------------------------- 1 | name: Frontend.Blazor 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Web/Frontend.Blazor/**" 7 | - ".github/workflows/frontend.blazor.yml" 8 | - "eSchool.sln" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Web/Frontend.Blazor/**" 19 | - ".github/workflows/frontend.blazor.yml" 20 | - "eSchool.sln" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | 31 | build: 32 | 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - uses: actions/checkout@v1 37 | - name: docker compose 38 | run: docker-compose build frontend.blazor 39 | -------------------------------------------------------------------------------- /.github/workflows/library-management.api.yml: -------------------------------------------------------------------------------- 1 | name: LibraryManagement.API 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Services/LibraryManagement/**" 7 | - ".github/workflows/library-management.api.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Services/LibraryManagement/**" 19 | - ".github/workflows/library-management.api.yml" 20 | - "src/Libraries/OpenTelemetry/**" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | build: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v1 35 | 36 | - name: set image tag 37 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 38 | 39 | - name: build 40 | run: docker-compose build librarymanagement.api 41 | 42 | - uses: azure/docker-login@v1 43 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 44 | with: 45 | login-server: index.docker.io 46 | username: ${{ secrets.DOCKERIO_USERNAME }} 47 | password: ${{ secrets.DOCKERIO_PASSWORD }} 48 | 49 | - name: push image 50 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 51 | run: docker-compose push librarymanagement.api 52 | -------------------------------------------------------------------------------- /.github/workflows/result-processing.api.yml: -------------------------------------------------------------------------------- 1 | name: ResultProcessing.API 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Services/ResultProcessing/**" 7 | - ".github/workflows/result-processing.api.yml" 8 | - "src/Libraries/OpenTelemetry/**" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Services/ResultProcessing/**" 19 | - ".github/workflows/result-processing.api.yml" 20 | - "src/Libraries/OpenTelemetry/**" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | build: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v1 35 | 36 | - name: set image tag 37 | run: echo "TAG=$(git tag --points-at HEAD | cut -c 2-4)" >> $GITHUB_ENV 38 | 39 | - name: build 40 | run: docker-compose build resultprocessing.api 41 | 42 | - uses: azure/docker-login@v1 43 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 44 | with: 45 | login-server: index.docker.io 46 | username: ${{ secrets.DOCKERIO_USERNAME }} 47 | password: ${{ secrets.DOCKERIO_PASSWORD }} 48 | 49 | - name: push image 50 | if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' 51 | run: docker-compose push resultprocessing.api 52 | -------------------------------------------------------------------------------- /.github/workflows/sanitycheck.yml: -------------------------------------------------------------------------------- 1 | name: sanitycheck 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | misspell: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: check out code 13 | uses: actions/checkout@v2 14 | 15 | - name: install misspell 16 | run: | 17 | curl -L -o ./install-misspell.sh https://git.io/misspell 18 | sh ./install-misspell.sh 19 | 20 | - name: run misspell 21 | run: ./bin/misspell -error . -------------------------------------------------------------------------------- /.github/workflows/webstatus.yml: -------------------------------------------------------------------------------- 1 | name: WebStatus 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/Web/WebStatus/**" 7 | - ".github/workflows/webstatus.yml" 8 | - "eSchool.sln" 9 | - "build/dotnet/**" 10 | - "src/Directory.Build.props" 11 | - "docker-compose.yml" 12 | - "docker-compose.override.yml" 13 | - "docker-compose.dcproj" 14 | - "docker-compose-test.yml" 15 | - "docker-compose-test.override.yml" 16 | pull_request: 17 | paths: 18 | - "src/Web/WebStatus/**" 19 | - ".github/workflows/webstatus.yml" 20 | - "eSchool.sln" 21 | - "build/dotnet/**" 22 | - "src/Directory.Build.props" 23 | - "docker-compose.yml" 24 | - "docker-compose.override.yml" 25 | - "docker-compose.dcproj" 26 | - "docker-compose-test.yml" 27 | - "docker-compose-test.override.yml" 28 | 29 | jobs: 30 | 31 | build: 32 | 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - uses: actions/checkout@v1 37 | - name: docker compose 38 | run: docker-compose build webstatus 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [ 3 | 80, 4 | 120 5 | ] 6 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [techcombd@outlook.com](mailto:techcombd@outlook.com). 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to eSchool 2 | 3 | We would love for you to contribute to eSchool and help make it even better 4 | than it is today! As a contributor, here are the guidelines we would like you 5 | to follow: 6 | 7 | - [Code of Conduct](#coc) 8 | - [Contribution Guideline](#contribution-guideline) 9 | - [Got a Question, Problem or idea](#question) 10 | 11 | ## Code of Conduct 12 | Help us keep eSchool open and inclusive. Please read and follow our [Code of Conduct][coc]. 13 | 14 | ## Contribution Guideline 15 | If you are first time contributing to open source then please check out our another project [First-contribution][first-contribution]. You can also check the [Contribution Guideline][contribution-guideline] 16 | 17 | ## Got a Question, Problem or idea? 18 | If you have any question or face problem with the project or just simply want to share your great idea about how we can improve this project, then feel free to create an issue [here][issue]. 19 | 20 | [coc]: https://github.com/OpenCodeFoundation/eSchool/blob/master/CODE_OF_CONDUCT.md 21 | [first-contribution]: https://github.com/OpenCodeFoundation/first-contributions 22 | [contribution-guideline]: https://github.com/OpenCodeFoundation/first-contributions/blob/master/CONTRIBUTING.md 23 | [issue]: https://github.com/OpenCodeFoundation/eSchool/issues 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 OpenCode Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build/dotnet/Common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9.0 4 | true 5 | 6 | enable 7 | 8 | $(MSBuildThisFileDirectory)/eSchool.ruleset 9 | 10 | 11 | 14 | 15 | 16 | 20 | [3.3.0,4.0) 21 | [16.9.1] 22 | [1.2.0-*,2.0) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | All 32 | 33 | 34 | 35 | all 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /build/dotnet/eSchool.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /build/dotnet/stylecop.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 4 | "settings": { 5 | "documentationRules": { 6 | "companyName": "OpenCode Foundation", 7 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the MIT License. See LICENSE in the project root for license information.", 8 | "xmlHeader": false, 9 | "documentInterfaces": false, 10 | "documentInternalElements": false, 11 | "documentExposedElements": false 12 | }, 13 | "orderingRules": { 14 | "usingDirectivesPlacement": "outsideNamespace" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /deploy/k8s/charts/README.md: -------------------------------------------------------------------------------- 1 | # Deployment Guideline 2 | 3 | 1. Install istio with demo profile from [getting started](https://istio.io/latest/docs/setup/getting-started/) tutorial. 4 | 2. Create new namespace, eschool - `kubectl create ns eschool` 5 | 3. Enable istio sidecar injection in eschool namespace - `kubectl label namespace eschool istio-injection=enabled` 6 | 7 | 4. Deploy the eschool gateway `kubectl -n eschool apply -f .\eschool-gateway.yaml` 8 | 9 | 5. Deploy mssql - `helm -n eschool install mssql .\mssql\` 10 | 11 | 6. Deploy enrolling service - `helm -n eschool install enrolling .\enrolling\` 12 | 13 | 7. After the deployment is completed visit [http://localhost/enrolling-api/swagger/index.html](http://localhost/enrolling-api/swagger/index.html) -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: enrolling 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 0.2.0 24 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "enrolling.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "enrolling.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "enrolling.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "enrolling.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "enrolling.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "enrolling.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "enrolling.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "enrolling.labels" -}} 38 | helm.sh/chart: {{ include "enrolling.chart" . }} 39 | {{ include "enrolling.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "enrolling.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "enrolling.name" . }} 51 | app: {{ include "enrolling.name" . }} 52 | version: {{ .Chart.AppVersion | quote }} 53 | app.kubernetes.io/instance: {{ .Release.Name }} 54 | {{- end }} 55 | 56 | {{/* 57 | Create the name of the service account to use 58 | */}} 59 | {{- define "enrolling.serviceAccountName" -}} 60 | {{- if .Values.serviceAccount.create }} 61 | {{- default (include "enrolling.fullname" .) .Values.serviceAccount.name }} 62 | {{- else }} 63 | {{- default "default" .Values.serviceAccount.name }} 64 | {{- end }} 65 | {{- end }} 66 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "enrolling.fullname" . }} 5 | labels: 6 | {{- include "enrolling.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "enrolling.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "enrolling.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "enrolling.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | env: 37 | - name: PATH_BASE 38 | value: {{ .Values.istio.prefix }} 39 | - name: ConnectionStrings 40 | value: {{ .Values.database.sql.connectionString | quote }} 41 | - name: OpenTelemetry__Istio 42 | value: "true" 43 | - name: OpenTelemetry__Jaeger__Host 44 | value: {{ .Values.jaeger.host }} 45 | ports: 46 | - name: http 47 | containerPort: 80 48 | protocol: TCP 49 | {{ if .Values.probes -}} 50 | {{- if .Values.probes.liveness -}} 51 | livenessProbe: 52 | httpGet: 53 | port: {{ .Values.probes.liveness.port }} 54 | path: {{ .Values.probes.liveness.path }} 55 | initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} 56 | periodSeconds: {{ .Values.probes.liveness.periodSeconds }} 57 | {{- end -}} 58 | {{- end -}} 59 | {{- if .Values.probes -}} 60 | {{- if .Values.probes.readiness }} 61 | readinessProbe: 62 | httpGet: 63 | port: {{ .Values.probes.readiness.port }} 64 | path: {{ .Values.probes.readiness.path }} 65 | initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} 66 | periodSeconds: {{ .Values.probes.readiness.periodSeconds }} 67 | timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} 68 | {{- end -}} 69 | {{- end }} 70 | resources: 71 | {{- toYaml .Values.resources | nindent 12 }} 72 | {{- with .Values.nodeSelector }} 73 | nodeSelector: 74 | {{- toYaml . | nindent 8 }} 75 | {{- end }} 76 | {{- with .Values.affinity }} 77 | affinity: 78 | {{- toYaml . | nindent 8 }} 79 | {{- end }} 80 | {{- with .Values.tolerations }} 81 | tolerations: 82 | {{- toYaml . | nindent 8 }} 83 | {{- end }} 84 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "enrolling.fullname" . }} 6 | labels: 7 | {{- include "enrolling.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "enrolling.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "enrolling.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "enrolling.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "enrolling.fullname" . }} 5 | labels: 6 | {{- include "enrolling.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "enrolling.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "enrolling.serviceAccountName" . }} 6 | labels: 7 | {{- include "enrolling.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "enrolling.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "enrolling.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "enrolling.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/templates/virtualservice.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.istio.enabled -}} 2 | {{- $fullName := include "enrolling.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | apiVersion: networking.istio.io/v1alpha3 5 | kind: VirtualService 6 | metadata: 7 | name: {{ $fullName }} 8 | spec: 9 | hosts: 10 | {{- range .Values.istio.hosts }} 11 | - {{ .host | quote }} 12 | {{- end }} 13 | gateways: 14 | - {{ .Values.istio.gateway }} 15 | http: 16 | - name: {{ $fullName | quote }} 17 | match: 18 | - uri: 19 | prefix: {{ .Values.istio.prefix | quote }} 20 | route: 21 | - destination: 22 | host: {{ $fullName }} 23 | port: 24 | number: {{ $svcPort }} 25 | {{- end }} -------------------------------------------------------------------------------- /deploy/k8s/charts/enrolling/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for enrolling. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | pathBase: /enrolling-api 7 | 8 | image: 9 | repository: eschool/enrolling.api 10 | pullPolicy: IfNotPresent 11 | # Overrides the image tag whose default is the chart appVersion. 12 | tag: "latest" 13 | 14 | imagePullSecrets: [] 15 | nameOverride: "" 16 | fullnameOverride: "" 17 | 18 | serviceAccount: 19 | # Specifies whether a service account should be created 20 | create: true 21 | # Annotations to add to the service account 22 | annotations: {} 23 | # The name of the service account to use. 24 | # If not set and create is true, a name is generated using the fullname template 25 | name: "" 26 | 27 | podAnnotations: {} 28 | 29 | podSecurityContext: {} 30 | # fsGroup: 2000 31 | 32 | securityContext: {} 33 | # capabilities: 34 | # drop: 35 | # - ALL 36 | # readOnlyRootFilesystem: true 37 | # runAsNonRoot: true 38 | # runAsUser: 1000 39 | 40 | service: 41 | type: ClusterIP 42 | port: 80 43 | 44 | ingress: 45 | enabled: false 46 | annotations: {} 47 | # kubernetes.io/ingress.class: nginx 48 | # kubernetes.io/tls-acme: "true" 49 | hosts: 50 | - host: chart-example.local 51 | paths: [] 52 | tls: [] 53 | # - secretName: chart-example-tls 54 | # hosts: 55 | # - chart-example.local 56 | 57 | istio: 58 | enabled: true 59 | gateway: eschool-gateway 60 | prefix: /enrolling-api 61 | hosts: 62 | - host: "*" 63 | jaeger: 64 | host: jaeger-agent.istio-system.svc.cluster.local 65 | 66 | database: 67 | sql: 68 | connectionString: "Server=mssql;Database=OpenCodeFoundation.EnrollingDb;User Id=sa;Password=Pass@word1" 69 | 70 | resources: 71 | # We usually recommend not to specify default resources and to leave this as a conscious 72 | # choice for the user. This also increases chances charts run on environments with little 73 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 74 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 75 | limits: 76 | cpu: 100m 77 | memory: 128Mi 78 | requests: 79 | cpu: 100m 80 | memory: 128Mi 81 | 82 | probes: 83 | liveness: 84 | path: /liveness 85 | initialDelaySeconds: 10 86 | periodSeconds: 15 87 | port: 80 88 | readiness: 89 | path: /hc 90 | timeoutSeconds: 5 91 | initialDelaySeconds: 90 92 | periodSeconds: 60 93 | port: 80 94 | 95 | autoscaling: 96 | enabled: false 97 | minReplicas: 1 98 | maxReplicas: 100 99 | targetCPUUtilizationPercentage: 80 100 | # targetMemoryUtilizationPercentage: 80 101 | 102 | nodeSelector: {} 103 | 104 | tolerations: [] 105 | 106 | affinity: {} 107 | -------------------------------------------------------------------------------- /deploy/k8s/charts/eschool-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: eschool-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use istio default controller 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" 15 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: mssql 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 1.16.0 24 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "mssql.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "mssql.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "mssql.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "mssql.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "mssql.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "mssql.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "mssql.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "mssql.labels" -}} 38 | helm.sh/chart: {{ include "mssql.chart" . }} 39 | {{ include "mssql.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "mssql.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "mssql.name" . }} 51 | app: {{ include "mssql.name" . }} 52 | app.kubernetes.io/instance: {{ .Release.Name }} 53 | {{- end }} 54 | 55 | {{/* 56 | Create the name of the service account to use 57 | */}} 58 | {{- define "mssql.serviceAccountName" -}} 59 | {{- if .Values.serviceAccount.create }} 60 | {{- default (include "mssql.fullname" .) .Values.serviceAccount.name }} 61 | {{- else }} 62 | {{- default "default" .Values.serviceAccount.name }} 63 | {{- end }} 64 | {{- end }} 65 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "mssql.fullname" . }} 5 | labels: 6 | {{- include "mssql.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "mssql.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "mssql.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "mssql.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | env: 37 | - name: ACCEPT_EULA 38 | value: "Y" 39 | - name: MSSQL_PID 40 | value: "Developer" 41 | - name: SA_PASSWORD 42 | value: "Pass@word1" 43 | ports: 44 | - name: tcp 45 | containerPort: 1433 46 | protocol: TCP 47 | resources: 48 | {{- toYaml .Values.resources | nindent 12 }} 49 | {{- with .Values.nodeSelector }} 50 | nodeSelector: 51 | {{- toYaml . | nindent 8 }} 52 | {{- end }} 53 | {{- with .Values.affinity }} 54 | affinity: 55 | {{- toYaml . | nindent 8 }} 56 | {{- end }} 57 | {{- with .Values.tolerations }} 58 | tolerations: 59 | {{- toYaml . | nindent 8 }} 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/destinationrule.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "mssql.fullname" . -}} 2 | apiVersion: networking.istio.io/v1alpha3 3 | kind: DestinationRule 4 | metadata: 5 | name: {{ $fullName }} 6 | spec: 7 | host: {{ $fullName }} 8 | trafficPolicy: 9 | tls: 10 | mode: ISTIO_MUTUAL -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "mssql.fullname" . }} 6 | labels: 7 | {{- include "mssql.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "mssql.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "mssql.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "mssql.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/peerauthentication.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "mssql.fullname" . -}} 2 | apiVersion: security.istio.io/v1beta1 3 | kind: PeerAuthentication 4 | metadata: 5 | name: {{ $fullName }} 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: {{ $fullName }} 10 | mtls: 11 | mode: STRICT -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "mssql.fullname" . }} 5 | labels: 6 | {{- include "mssql.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: tcp 12 | protocol: TCP 13 | name: tcp 14 | selector: 15 | {{- include "mssql.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "mssql.serviceAccountName" . }} 6 | labels: 7 | {{- include "mssql.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "mssql.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "mssql.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "mssql.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /deploy/k8s/charts/mssql/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for mssql. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: mcr.microsoft.com/mssql/server 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart appVersion. 11 | tag: "2019-latest" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Annotations to add to the service account 21 | annotations: {} 22 | # The name of the service account to use. 23 | # If not set and create is true, a name is generated using the fullname template 24 | name: "" 25 | 26 | podAnnotations: {} 27 | 28 | podSecurityContext: {} 29 | # fsGroup: 2000 30 | 31 | securityContext: {} 32 | # capabilities: 33 | # drop: 34 | # - ALL 35 | # readOnlyRootFilesystem: true 36 | # runAsNonRoot: true 37 | # runAsUser: 1000 38 | 39 | service: 40 | type: ClusterIP 41 | port: 1433 42 | 43 | sql: 44 | password: Pass@word 45 | 46 | ingress: 47 | enabled: false 48 | annotations: {} 49 | # kubernetes.io/ingress.class: nginx 50 | # kubernetes.io/tls-acme: "true" 51 | hosts: 52 | - host: chart-example.local 53 | paths: [] 54 | tls: [] 55 | # - secretName: chart-example-tls 56 | # hosts: 57 | # - chart-example.local 58 | 59 | resources: {} 60 | # We usually recommend not to specify default resources and to leave this as a conscious 61 | # choice for the user. This also increases chances charts run on environments with little 62 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 63 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 64 | # limits: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | # requests: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | 71 | autoscaling: 72 | enabled: false 73 | minReplicas: 1 74 | maxReplicas: 100 75 | targetCPUUtilizationPercentage: 80 76 | # targetMemoryUtilizationPercentage: 80 77 | 78 | nodeSelector: {} 79 | 80 | tolerations: [] 81 | 82 | affinity: {} 83 | -------------------------------------------------------------------------------- /deploy/k8s/charts/setup.bat: -------------------------------------------------------------------------------- 1 | kubectl create ns eschool 2 | kubectl label namespace eschool istio-injection=enabled 3 | kubectl -n eschool apply -f .\eschool-gateway.yaml 4 | helm -n eschool install mssql .\mssql\ 5 | helm -n eschool install enrolling .\enrolling\ -------------------------------------------------------------------------------- /docker-compose-test.override.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | sql.data.test: 5 | environment: 6 | - SA_PASSWORD=Pass@word 7 | - ACCEPT_EULA=Y 8 | ports: 9 | - "5433:1433" # Important: In a production environment your should remove the external port 10 | 11 | enrolling.api.test: 12 | entrypoint: 13 | - dotnet 14 | - test 15 | - --results-directory 16 | - /tests 17 | - --logger 18 | - trx;LogFileName=/tests/enrolling-unit-test-results.xml 19 | - --collect:"XPlat Code Coverage" 20 | 21 | enrolling.api.functional-test: 22 | environment: 23 | - ASPNETCORE_ENVIRONMENT=Development 24 | - ASPNETCORE_URLS=http://0.0.0.0:80 25 | - ConnectionStrings=Server=sql.data.test;Database=OpenCodeFoundation.EnrollingDb;User Id=sa;Password=Pass@word 26 | ports: 27 | - "5102:80" 28 | entrypoint: 29 | - dotnet 30 | - test 31 | - --results-directory 32 | - /tests 33 | - --logger 34 | - trx;LogFileName=/tests/enrolling-functional-test-results.xml 35 | - --collect:"XPlat Code Coverage" 36 | -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | sql.data.test: 5 | image: mcr.microsoft.com/mssql/server:2019-latest 6 | 7 | enrolling.api.test: 8 | image: ${REGISTRY:-eschool}/enrolling.api.test:${TAG:-latest} 9 | build: 10 | context: . 11 | dockerfile: src/Services/Enrolling/Enrolling.API/Dockerfile 12 | target: unittest 13 | volumes: 14 | - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests 15 | 16 | enrolling.api.functional-test: 17 | image: ${REGISTRY:-eschool}/enrolling.api.functional-test:${TAG:-latest} 18 | build: 19 | context: . 20 | dockerfile: src/Services/Enrolling/Enrolling.API/Dockerfile 21 | target: functionaltest 22 | depends_on: 23 | - sql.data.test 24 | volumes: 25 | - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests 26 | -------------------------------------------------------------------------------- /docker-compose.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1 5 | Linux 6 | {9CC3C97F-A73C-4ECB-B920-626035DA3013} 7 | True 8 | http://localhost:5102/swagger 9 | enrolling.api 10 | 11 | 12 | 13 | docker-compose.yml 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | seq: 5 | environment: 6 | - ACCEPT_EULA=Y 7 | ports: 8 | - "5140:80" 9 | 10 | sql.data: 11 | environment: 12 | - SA_PASSWORD=Pass@word 13 | - ACCEPT_EULA=Y 14 | ports: 15 | - "5433:1433" # Important: In a production environment your should remove the external port 16 | 17 | enrolling.api: 18 | environment: 19 | - ASPNETCORE_ENVIRONMENT=Development 20 | - ASPNETCORE_URLS=http://0.0.0.0:80 21 | - ConnectionStrings=Server=sql.data;Database=OpenCodeFoundation.EnrollingDb;User Id=sa;Password=Pass@word 22 | ports: 23 | - "5102:80" 24 | - "5581:5001" 25 | 26 | certificateprocessing.api: 27 | environment: 28 | - ASPNETCORE_ENVIRONMENT=Development 29 | - ASPNETCORE_URLS=http://0.0.0.0:80 30 | ports: 31 | - "5103:80" 32 | 33 | exammanagement.api: 34 | environment: 35 | - ASPNETCORE_ENVIRONMENT=Development 36 | - ASPNETCORE_URLS=http://0.0.0.0:80 37 | ports: 38 | - "5104:80" 39 | 40 | librarymanagement.api: 41 | environment: 42 | - ASPNETCORE_ENVIRONMENT=Development 43 | - ASPNETCORE_URLS=http://0.0.0.0:80 44 | ports: 45 | - "5105:80" 46 | 47 | resultprocessing.api: 48 | environment: 49 | - ASPNETCORE_ENVIRONMENT=Development 50 | - ASPNETCORE_URLS=http://0.0.0.0:80 51 | ports: 52 | - "5106:80" 53 | 54 | webstatus: 55 | environment: 56 | - ASPNETCORE_ENVIRONMENT=Development 57 | - ASPNETCORE_URLS=http://0.0.0.0:80 58 | - ConnectionStrings=Server=sql.data;Database=OpenCodeFoundation.WebStatusDb;User Id=sa;Password=Pass@word 59 | - HealthChecksUI__HealthChecks__0__Name=Enrolling HTTP Check 60 | - HealthChecksUI__HealthChecks__0__Uri=http://enrolling.api/hc 61 | ports: 62 | - "5107:80" 63 | 64 | eschool.graphql: 65 | environment: 66 | - ASPNETCORE_ENVIRONMENT=Development 67 | - ASPNETCORE_URLS=http://0.0.0.0:80 68 | ports: 69 | - "5101:80" 70 | 71 | frontend.blazor: 72 | environment: 73 | - ASPNETCORE_ENVIRONMENT=Development 74 | - ASPNETCORE_URLS=http://0.0.0.0:80 75 | ports: 76 | - "5200:80" 77 | 78 | jaeger: 79 | environment: 80 | - COLLECTOR_ZIPKIN_HTTP_PORT=9411 81 | ports: 82 | - 5775:5775/udp 83 | - 6831:6831/udp 84 | - 6832:6832/udp 85 | - 5778:5778 86 | - 16686:16686 87 | - 14268:14268 88 | - 14250:14250 89 | - 9411:9411 90 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | seq: 5 | image: datalust/seq:latest 6 | 7 | sql.data: 8 | image: mcr.microsoft.com/mssql/server:2019-latest 9 | 10 | enrolling.api: 11 | image: ${REGISTRY:-eschool}/enrolling.api:${TAG:-latest} 12 | build: 13 | context: . 14 | dockerfile: src/Services/Enrolling/Enrolling.API/Dockerfile 15 | depends_on: 16 | - sql.data 17 | 18 | certificateprocessing.api: 19 | image: ${REGISTRY:-eschool}/certificateprocessing.api:${TAG:-latest} 20 | build: 21 | context: . 22 | dockerfile: src/Services/CertificateProcessing/CertificateProcessing.API/Dockerfile 23 | depends_on: 24 | - sql.data 25 | 26 | exammanagement.api: 27 | image: ${REGISTRY:-eschool}/exammanagement.api:${TAG:-latest} 28 | build: 29 | context: . 30 | dockerfile: src/Services/ExamManagement/ExamManagement.API/Dockerfile 31 | depends_on: 32 | - sql.data 33 | 34 | librarymanagement.api: 35 | image: ${REGISTRY:-eschool}/librarymanagement.api:${TAG:-latest} 36 | build: 37 | context: . 38 | dockerfile: src/Services/LibraryManagement/LibraryManagement.API/Dockerfile 39 | depends_on: 40 | - sql.data 41 | 42 | resultprocessing.api: 43 | image: ${REGISTRY:-eschool}/resultprocessing.api:${TAG:-latest} 44 | build: 45 | context: . 46 | dockerfile: src/Services/ResultProcessing/ResultProcessing.API/Dockerfile 47 | depends_on: 48 | - sql.data 49 | 50 | webstatus: 51 | image: ${REGISTRY:-eschool}/webstatus:${TAG:-latest} 52 | build: 53 | context: . 54 | dockerfile: src/Web/WebStatus/Dockerfile 55 | 56 | eschool.graphql: 57 | image: ${REGISTRY:-eschool}/eschool.graphql:${TAG:-latest} 58 | build: 59 | context: . 60 | dockerfile: src/ApiGateways/eSchool.GraphQL/Dockerfile 61 | 62 | frontend.blazor: 63 | image: ${REGISTRY:-eschool}/frontend.blazor:${TAG:-latest} 64 | build: 65 | context: . 66 | dockerfile: src/Web/Frontend.Blazor/Frontend.Blazor.Server/Dockerfile 67 | 68 | jaeger: 69 | image: jaegertracing/all-in-one 70 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/ApiGateways/eSchool.GraphQL/eSchool.GraphQL.csproj", "src/ApiGateways/eSchool.GraphQL/"] 11 | COPY ["src/Libraries/OpenTelemetry/OpenTelemetry.csproj", "src/Libraries/OpenTelemetry/"] 12 | 13 | RUN dotnet restore "src/ApiGateways/eSchool.GraphQL/eSchool.GraphQL.csproj" 14 | 15 | COPY . . 16 | WORKDIR "/src/src/ApiGateways/eSchool.GraphQL" 17 | RUN dotnet build "eSchool.GraphQL.csproj" -c Release -o /app/build 18 | 19 | FROM build AS publish 20 | RUN dotnet publish "eSchool.GraphQL.csproj" -c Release -o /app/publish 21 | 22 | FROM base AS final 23 | WORKDIR /app 24 | COPY --from=publish /app/publish . 25 | ENTRYPOINT ["dotnet", "ESchool.GraphQL.dll"] -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/Enrollings/EnrollingQueries.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using HotChocolate; 6 | using HotChocolate.Types; 7 | using OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrolling; 8 | 9 | namespace OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrollings 10 | { 11 | [ExtendObjectType(OperationTypeNames.Query)] 12 | public class EnrollingQueries 13 | { 14 | public async Task> GetEnrollmentsAsync( 15 | [Service] IEnrollingServiceClient client, 16 | CancellationToken cancellationToken = default) 17 | { 18 | if (client == null) 19 | { 20 | throw new ArgumentNullException(nameof(client)); 21 | } 22 | 23 | return await client.GetAllAsync(cancellationToken).ConfigureAwait(false); 24 | } 25 | 26 | public async Task GetEnrollmentAsync( 27 | Guid enrollmentId, 28 | [Service] IEnrollingServiceClient client, 29 | CancellationToken cancellationToken = default) 30 | { 31 | if (client == null) 32 | { 33 | throw new ArgumentNullException(nameof(client)); 34 | } 35 | 36 | return await client.GetByIdAsync(enrollmentId, cancellationToken) 37 | .ConfigureAwait(false); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/Enrollings/EnrollmentMutations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using HotChocolate; 5 | using HotChocolate.Types; 6 | using OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrolling; 7 | 8 | namespace OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrollings 9 | { 10 | [ExtendObjectType(OperationTypeNames.Mutation)] 11 | public class EnrollmentMutations 12 | { 13 | public async Task CreateEnrollmentAsync( 14 | EnrollmentApplicationCommand enrollment, 15 | [Service] IEnrollingServiceClient client, 16 | CancellationToken cancellationToken = default) 17 | { 18 | if (client == null) 19 | { 20 | throw new ArgumentNullException(nameof(client)); 21 | } 22 | 23 | return await client.CreateAsync(enrollment, cancellationToken).ConfigureAwait(false); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Hosting; 7 | using Serilog; 8 | using Serilog.Enrichers.Span; 9 | 10 | namespace OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL 11 | { 12 | public static class Program 13 | { 14 | public static readonly string Namespace = typeof(Program).Namespace!; 15 | public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); 16 | 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 18 | "Design", 19 | "CA1031:Do not catch general exception types", 20 | Justification = "Top level all exception catcher")] 21 | public static int Main(string[] args) 22 | { 23 | Activity.DefaultIdFormat = ActivityIdFormat.W3C; 24 | 25 | var configuration = GetConfiguration(); 26 | 27 | Log.Logger = CreateSerilogLogger(configuration); 28 | 29 | try 30 | { 31 | Log.Information("Configuring web host ({ApplicationContext})...", AppName); 32 | var host = CreateHostBuilder(configuration, args).Build(); 33 | 34 | Log.Information("Starting web host ({ApplicationContext})...", AppName); 35 | host.Run(); 36 | 37 | return 0; 38 | } 39 | catch (Exception ex) 40 | { 41 | Log.Fatal(ex, "Host terminated unexpectedly"); 42 | return 1; 43 | } 44 | finally 45 | { 46 | Log.CloseAndFlush(); 47 | } 48 | } 49 | 50 | public static IHostBuilder CreateHostBuilder(IConfiguration configuration, string[] args) => 51 | Host.CreateDefaultBuilder(args) 52 | .ConfigureWebHostDefaults(webBuilder => 53 | { 54 | webBuilder.UseStartup(); 55 | webBuilder.UseConfiguration(configuration); 56 | webBuilder.UseSerilog(); 57 | }); 58 | 59 | private static ILogger CreateSerilogLogger(IConfiguration configuration) 60 | { 61 | return new LoggerConfiguration() 62 | .MinimumLevel.Verbose() 63 | .Enrich.WithProperty("ApplicationContext", AppName) 64 | .Enrich.FromLogContext() 65 | .Enrich.WithSpan() 66 | .WriteTo.Console() 67 | .WriteTo.Seq("http://seq") 68 | .ReadFrom.Configuration(configuration) 69 | .CreateLogger(); 70 | } 71 | 72 | private static IConfiguration GetConfiguration() 73 | { 74 | var builder = new ConfigurationBuilder() 75 | .SetBasePath(Directory.GetCurrentDirectory()) 76 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 77 | .AddEnvironmentVariables(); 78 | 79 | // Load other configurations here. Ex. Keyvault or AppConfiguration 80 | return builder.Build(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/RestApiErrorMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HotChocolate; 4 | using HotChocolate.Resolvers; 5 | using OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrolling; 6 | 7 | namespace OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL 8 | { 9 | public class RestApiErrorMiddleware 10 | { 11 | private readonly FieldDelegate _next; 12 | 13 | public RestApiErrorMiddleware(FieldDelegate next) 14 | { 15 | _next = next ?? throw new ArgumentNullException(nameof(next)); 16 | } 17 | 18 | public async Task Invoke(IMiddlewareContext context) 19 | { 20 | if (context == null) 21 | { 22 | throw new ArgumentNullException(nameof(context)); 23 | } 24 | 25 | try 26 | { 27 | await _next(context).ConfigureAwait(false); 28 | } 29 | catch (ApiException exception) 30 | { 31 | var errors = exception.Result.Errors; 32 | foreach (var validationErrors in errors) 33 | { 34 | foreach (var validationError in validationErrors.Value) 35 | { 36 | context.ReportError(ErrorBuilder 37 | .New() 38 | .SetMessage(validationError) 39 | .SetPath(context.Path) 40 | .SetExtension("field", validationErrors.Key) 41 | .SetExtension("extra", exception.Result.AdditionalProperties) 42 | .Build()); 43 | } 44 | } 45 | } 46 | catch (ApiException exception) 47 | { 48 | context.ReportError(ErrorBuilder 49 | .New() 50 | .SetMessage(exception.Result.Title) 51 | .SetPath(context.Path) 52 | .SetExtension("extra", exception.Result.AdditionalProperties) 53 | .Build()); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrolling; 7 | using OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrollings; 8 | using OpenCodeFoundation.OpenTelemetry; 9 | using Serilog; 10 | 11 | namespace OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL 12 | { 13 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 14 | "Performance", 15 | "CA1822:Mark members as static", 16 | Justification = "Can not mark this class static. We can remove it when we read configuration")] 17 | public class Startup 18 | { 19 | public const string Enrolling = "enrolling"; 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddCors(); 26 | 27 | services.AddHttpClient(client => 28 | { 29 | client.BaseAddress = new Uri("http://enrolling.api"); 30 | }); 31 | 32 | services 33 | .AddGraphQLServer() 34 | .AddQueryType() 35 | .AddTypeExtension() 36 | .AddMutationType() 37 | .AddTypeExtension() 38 | .UseField(); 39 | 40 | services.AddOpenTelemetryIntegration(); 41 | } 42 | 43 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 44 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 45 | { 46 | if (env.IsDevelopment()) 47 | { 48 | app.UseDeveloperExceptionPage(); 49 | } 50 | 51 | app.UseSerilogRequestLogging(); 52 | 53 | app.UseCors(o => o 54 | .AllowAnyHeader() 55 | .AllowAnyMethod() 56 | .AllowAnyOrigin()); 57 | 58 | app.UseRouting(); 59 | 60 | app.UseEndpoints(endpoints => 61 | { 62 | endpoints.MapGraphQL(); 63 | }); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Information", 5 | "Override": { 6 | "Microsoft": "Warning", 7 | "OpenCodeFoundation": "Information", 8 | "System": "Warning" 9 | } 10 | } 11 | }, 12 | "AllowedHosts": "*", 13 | "OpenTelemetry": { 14 | "Enabled": true, 15 | "Istio": false, 16 | "Jaeger": { 17 | "Enabled": true, 18 | "ServiceName": "eschool.graphql", 19 | "Host": "jaeger", 20 | "Port": 6831 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ApiGateways/eSchool.GraphQL/eSchool.GraphQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | ESchool.GraphQL 5 | OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | /GenerateClientInterfaces:true /UseBaseUrl:false /OperationGenerationMode:SingleClientFromOperationId /GenerateExceptionClasses:true /GenerateOptionalPropertiesAsNullable:false /generateNullableReferenceTypes:true 27 | EnrollingServiceClient 28 | OpenCodeFoundation.ESchool.ApiGateways.ESchool.GraphQL.Enrolling 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Libraries/OpenTelemetry/IConfigurationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace OpenCodeFoundation.OpenTelemetry 2 | { 3 | public interface IConfigurationOptions 4 | { 5 | public void Validate(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Libraries/OpenTelemetry/JaegerOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace OpenCodeFoundation.OpenTelemetry 4 | { 5 | public class JaegerOptions 6 | { 7 | [MemberNotNullWhen(true, nameof(ServiceName), nameof(Host))] 8 | public bool Enabled { get; set; } 9 | 10 | public string? ServiceName { get; set; } 11 | 12 | public string? Host { get; set; } 13 | 14 | public int? Port { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Libraries/OpenTelemetry/OpenTelemetry.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | OpenCodeFoundation.OpenTelemetry 5 | OpenCodeFoundation.OpenTelemetry 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Libraries/OpenTelemetry/OpenTelemetryOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OpenCodeFoundation.OpenTelemetry 4 | { 5 | public class OpenTelemetryOptions 6 | : IConfigurationOptions 7 | { 8 | public bool Enabled { get; set; } 9 | 10 | public bool AlwaysOnSampler { get; set; } = true; 11 | 12 | public bool Istio { get; set; } 13 | 14 | public JaegerOptions Jaeger { get; set; } = new(); 15 | 16 | public void Validate() 17 | { 18 | if (Jaeger.Enabled) 19 | { 20 | ValidateJaeger(); 21 | } 22 | } 23 | 24 | private void ValidateJaeger() 25 | { 26 | if (string.IsNullOrWhiteSpace(Jaeger.ServiceName)) 27 | { 28 | throw new ArgumentException("Jaeger service name can not be null if Jaeger is enabled"); 29 | } 30 | 31 | if (string.IsNullOrWhiteSpace(Jaeger.Host)) 32 | { 33 | throw new ArgumentException("Jaeger Host can not be null if Jaeger is enabled"); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Libraries/OpenTelemetry/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using OpenTelemetry; 6 | using OpenTelemetry.Context.Propagation; 7 | using OpenTelemetry.Resources; 8 | using OpenTelemetry.Trace; 9 | 10 | namespace OpenCodeFoundation.OpenTelemetry 11 | { 12 | public static class ServiceCollectionExtensions 13 | { 14 | private const string SectionName = "OpenTelemetry"; 15 | 16 | public static IServiceCollection AddOpenTelemetryIntegration( 17 | this IServiceCollection services, 18 | Action? options = null, 19 | string sectionName = SectionName) 20 | { 21 | var openTelemetryOptions = services.GetOptions(sectionName, options); 22 | 23 | if (openTelemetryOptions.Enabled) 24 | { 25 | ConfigureOpenTelemetry(services, openTelemetryOptions); 26 | } 27 | 28 | return services; 29 | } 30 | 31 | public static T GetOptions( 32 | this IServiceCollection services, 33 | string sectionName, 34 | Action? configure = null) 35 | where T : IConfigurationOptions, new() 36 | { 37 | var provider = services.BuildServiceProvider(); 38 | var configuration = provider.GetRequiredService(); 39 | 40 | var options = new T(); 41 | configure?.Invoke(options); 42 | 43 | configuration.GetSection(sectionName).Bind(options); 44 | 45 | options.Validate(); 46 | return options; 47 | } 48 | 49 | private static void ConfigureOpenTelemetry(IServiceCollection services, OpenTelemetryOptions openTelemetryOptions) 50 | { 51 | services.AddOpenTelemetryTracing(configure => 52 | { 53 | ConfigureSampler(openTelemetryOptions, configure); 54 | ConfigureInstrumentation(openTelemetryOptions, configure); 55 | ConfigureExporters(openTelemetryOptions, configure); 56 | }); 57 | } 58 | 59 | private static void ConfigureSampler(OpenTelemetryOptions openTelemetryOptions, TracerProviderBuilder configure) 60 | { 61 | if (openTelemetryOptions.AlwaysOnSampler) 62 | { 63 | configure.SetSampler(new AlwaysOnSampler()); 64 | } 65 | } 66 | 67 | private static void ConfigureExporters(OpenTelemetryOptions openTelemetryOptions, TracerProviderBuilder configure) 68 | { 69 | if (openTelemetryOptions.Jaeger.Enabled) 70 | { 71 | configure.SetResourceBuilder(ResourceBuilder.CreateDefault() 72 | .AddService(openTelemetryOptions.Jaeger.ServiceName)); 73 | 74 | configure.AddJaegerExporter(config => 75 | { 76 | config.AgentHost = openTelemetryOptions.Jaeger.Host; 77 | config.AgentPort = openTelemetryOptions.Jaeger.Port ?? 6831; 78 | }); 79 | } 80 | } 81 | 82 | private static void ConfigureInstrumentation(OpenTelemetryOptions openTelemetryOptions, TracerProviderBuilder configure) 83 | { 84 | Sdk.SetDefaultTextMapPropagator(GetPropagator(openTelemetryOptions)); 85 | 86 | configure.AddAspNetCoreInstrumentation(); 87 | 88 | configure.AddHttpClientInstrumentation(); 89 | 90 | configure.AddSqlClientInstrumentation(); 91 | } 92 | 93 | private static TextMapPropagator GetPropagator(OpenTelemetryOptions openTelemetryOptions) 94 | { 95 | var propagators = new List() 96 | { 97 | new TraceContextPropagator(), 98 | new BaggagePropagator(), 99 | }; 100 | 101 | if (openTelemetryOptions.Istio) 102 | { 103 | propagators.Add(new B3Propagator()); 104 | } 105 | 106 | return new CompositeTextMapPropagator(propagators); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/CertificateProcessing.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/Controllers/HelloWorldController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace CertificateProcessing.API.Controllers 4 | { 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class HelloWorldController : ControllerBase 8 | { 9 | [HttpGet] 10 | public ActionResult Ping() 11 | { 12 | return Ok("Pong"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/Services/CertificateProcessing/CertificateProcessing.API/CertificateProcessing.API.csproj", "src/Services/CertificateProcessing/CertificateProcessing.API/"] 11 | 12 | RUN dotnet restore "src/Services/CertificateProcessing/CertificateProcessing.API/CertificateProcessing.API.csproj" 13 | 14 | COPY . . 15 | WORKDIR "/src/src/Services/CertificateProcessing/CertificateProcessing.API" 16 | RUN dotnet build "CertificateProcessing.API.csproj" -c Release -o /app/build 17 | 18 | FROM build AS publish 19 | RUN dotnet publish "CertificateProcessing.API.csproj" -c Release -o /app/publish 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | ENTRYPOINT ["dotnet", "CertificateProcessing.API.dll"] -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Hosting; 7 | using Serilog; 8 | using Serilog.Enrichers.Span; 9 | 10 | namespace CertificateProcessing.API 11 | { 12 | public static class Program 13 | { 14 | public static readonly string Namespace = typeof(Program).Namespace!; 15 | public static readonly string AppName = Namespace[(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1)..]; 16 | 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 18 | "Design", 19 | "CA1031:Do not catch general exception types", 20 | Justification = "Top level all exception catcher")] 21 | public static int Main(string[] args) 22 | { 23 | Activity.DefaultIdFormat = ActivityIdFormat.W3C; 24 | 25 | var configuration = GetConfiguration(); 26 | Log.Logger = CreateSerilogLogger(configuration); 27 | 28 | try 29 | { 30 | Log.Information("Configuring web host ({ApplicationContext})...", AppName); 31 | var host = CreateHostBuilder(configuration, args).Build(); 32 | 33 | Log.Information("Starting web host ({ApplicationContext})...", AppName); 34 | host.Run(); 35 | 36 | return 0; 37 | } 38 | catch (Exception ex) 39 | { 40 | Log.Fatal(ex, "Host terminated unexpectedly"); 41 | return 1; 42 | } 43 | finally 44 | { 45 | Log.CloseAndFlush(); 46 | } 47 | } 48 | 49 | public static IHostBuilder CreateHostBuilder(IConfiguration configuration, string[] args) => 50 | Host.CreateDefaultBuilder(args) 51 | .ConfigureWebHostDefaults(webBuilder => 52 | { 53 | webBuilder.UseStartup(); 54 | webBuilder.UseConfiguration(configuration); 55 | webBuilder.UseSerilog(); 56 | }); 57 | 58 | private static ILogger CreateSerilogLogger(IConfiguration configuration) 59 | { 60 | return new LoggerConfiguration() 61 | .MinimumLevel.Verbose() 62 | .Enrich.WithProperty("ApplicationContext", AppName) 63 | .Enrich.FromLogContext() 64 | .Enrich.WithSpan() 65 | .WriteTo.Console() 66 | .WriteTo.Seq("http://seq") 67 | .ReadFrom.Configuration(configuration) 68 | .CreateLogger(); 69 | } 70 | 71 | private static IConfiguration GetConfiguration() 72 | { 73 | var builder = new ConfigurationBuilder() 74 | .SetBasePath(Directory.GetCurrentDirectory()) 75 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 76 | .AddEnvironmentVariables(); 77 | 78 | return builder.Build(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.OpenApi.Models; 8 | using Serilog; 9 | 10 | namespace CertificateProcessing.API 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers(); 25 | services.AddSwaggerGen(c => 26 | { 27 | c.SwaggerDoc( 28 | "v1", 29 | new OpenApiInfo 30 | { 31 | Title = "CertificateRegistration.API", 32 | Version = "v1", 33 | }); 34 | }); 35 | } 36 | 37 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 38 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 39 | { 40 | var pathBase = Configuration["PATH_BASE"]; 41 | if (!string.IsNullOrEmpty(pathBase)) 42 | { 43 | loggerFactory.CreateLogger().LogInformation("Using PATH BASE '{pathBase}'", pathBase); 44 | app.UsePathBase(pathBase); 45 | } 46 | 47 | if (env.IsDevelopment()) 48 | { 49 | app.UseDeveloperExceptionPage(); 50 | } 51 | 52 | app.UseSerilogRequestLogging(); 53 | app.UseSwagger(); 54 | app.UseSwaggerUI( 55 | c => c.SwaggerEndpoint( 56 | "/swagger/v1/swagger.json", "CertificateRegistration.API v1")); 57 | 58 | app.UseRouting(); 59 | 60 | app.UseAuthorization(); 61 | 62 | app.UseEndpoints(endpoints => 63 | { 64 | endpoints.MapControllers(); 65 | }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/CertificateProcessing/CertificateProcessing.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Behaviors/LoggingBehavior.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using MediatR; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Behaviors 7 | { 8 | public class LoggingBehavior 9 | : IPipelineBehavior 10 | where TRequest : notnull 11 | { 12 | private readonly ILogger> _logger; 13 | 14 | public LoggingBehavior( 15 | ILogger> logger) 16 | { 17 | _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); 18 | } 19 | 20 | public async Task Handle( 21 | TRequest request, 22 | CancellationToken cancellationToken, 23 | RequestHandlerDelegate next) 24 | { 25 | if (next is null) 26 | { 27 | throw new System.ArgumentNullException(nameof(next)); 28 | } 29 | 30 | _logger.LogInformation( 31 | "Handling request {RequestName} ({@Request})", 32 | request.GetType().Name, 33 | request); 34 | 35 | var response = await next().ConfigureAwait(false); 36 | 37 | _logger.LogInformation( 38 | "Request {RequestName} handled. Response: {@Response}", 39 | request.GetType().Name, 40 | response); 41 | 42 | return response; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Commands/EnrollmentApplicationCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 3 | 4 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Commands 5 | { 6 | public record EnrollmentApplicationCommand( 7 | string Name, 8 | string Email, 9 | string Mobile) 10 | : IRequest; 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Commands/EnrollmentApplicationCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 7 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 8 | 9 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Commands 10 | { 11 | public sealed class EnrollmentApplicationCommandHandler 12 | : IRequestHandler 13 | { 14 | private readonly ILogger _logger; 15 | private readonly EnrollingContext _context; 16 | 17 | public EnrollmentApplicationCommandHandler( 18 | EnrollingContext context, 19 | ILogger logger) 20 | { 21 | _context = context ?? throw new ArgumentNullException(nameof(context)); 22 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 23 | } 24 | 25 | public async Task Handle( 26 | EnrollmentApplicationCommand command, 27 | CancellationToken cancellationToken) 28 | { 29 | if (command == null) 30 | { 31 | throw new ArgumentNullException(nameof(command)); 32 | } 33 | 34 | var enrollment = Enrollment.CreateNew(command.Name, command.Email, command.Mobile); 35 | await _context.Enrollments.AddAsync(enrollment, cancellationToken) 36 | .ConfigureAwait(false); 37 | await _context.SaveChangesAsync(cancellationToken) 38 | .ConfigureAwait(false); 39 | return enrollment; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Queries/FindAllEnrollmentsHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | using Microsoft.EntityFrameworkCore; 6 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 7 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 8 | 9 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Queries 10 | { 11 | public class FindAllEnrollmentsHandler 12 | : IRequestHandler> 13 | { 14 | private readonly EnrollingContext _context; 15 | 16 | public FindAllEnrollmentsHandler(EnrollingContext context) 17 | { 18 | _context = context ?? throw new System.ArgumentNullException(nameof(context)); 19 | } 20 | 21 | public async Task> Handle( 22 | FindAllEnrollmentsQuery request, 23 | CancellationToken cancellationToken) 24 | { 25 | return await _context.Enrollments.ToListAsync(cancellationToken) 26 | .ConfigureAwait(false); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Queries/FindAllEnrollmentsQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MediatR; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 4 | 5 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Queries 6 | { 7 | public class FindAllEnrollmentsQuery 8 | : IRequest> 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Queries/GetEnrollmentByIdHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 6 | 7 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Queries 8 | { 9 | public class GetEnrollmentByIdHandler 10 | : IRequestHandler 11 | { 12 | private readonly IEnrollmentRepository _repository; 13 | 14 | public GetEnrollmentByIdHandler(IEnrollmentRepository repository) 15 | { 16 | _repository = repository ?? throw new ArgumentNullException(nameof(repository)); 17 | } 18 | 19 | public async Task Handle( 20 | GetEnrollmentByIdQuery query, 21 | CancellationToken cancellationToken) 22 | { 23 | return await _repository.FindByIdAsync(query.Id); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Queries/GetEnrollmentByIdQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 3 | 4 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Queries 5 | { 6 | public class GetEnrollmentByIdQuery 7 | : IRequest 8 | { 9 | public GetEnrollmentByIdQuery(EnrollmentId id) 10 | { 11 | Id = id; 12 | } 13 | 14 | public EnrollmentId Id { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Application/Validations/EnrollmentApplicationCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Commands; 3 | 4 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Validations 5 | { 6 | public class EnrollmentApplicationCommandValidator 7 | : AbstractValidator 8 | { 9 | public EnrollmentApplicationCommandValidator() 10 | { 11 | RuleFor(application => application.Name).NotEmpty(); 12 | RuleFor(application => application.Email).NotEmpty().EmailAddress(); 13 | RuleFor(application => application.Mobile).NotEmpty(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Controllers/EnrollmentsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using MediatR; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Commands; 9 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Queries; 10 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 11 | 12 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Controllers 13 | { 14 | [ApiController] 15 | [Route("[controller]")] 16 | public class EnrollmentsController : ControllerBase 17 | { 18 | private readonly ISender _sender; 19 | 20 | public EnrollmentsController(ISender sender) 21 | { 22 | _sender = sender ?? throw new ArgumentNullException(nameof(sender)); 23 | } 24 | 25 | [HttpGet(Name = "GetAll")] 26 | [ProducesResponseType(StatusCodes.Status200OK)] 27 | [ProducesDefaultResponseType] 28 | public async Task> Get(CancellationToken cancellationToken) 29 | => await _sender.Send(new FindAllEnrollmentsQuery(), cancellationToken) 30 | .ConfigureAwait(false); 31 | 32 | [HttpGet("{id}", Name = "GetById")] 33 | [ProducesResponseType(typeof(Enrollment), StatusCodes.Status200OK)] 34 | [ProducesResponseType(StatusCodes.Status404NotFound)] 35 | [ProducesDefaultResponseType] 36 | public async Task> GetById( 37 | EnrollmentId id, 38 | CancellationToken cancellationToken) 39 | { 40 | var enrollment = await _sender 41 | .Send(new GetEnrollmentByIdQuery(id), cancellationToken) 42 | .ConfigureAwait(false); 43 | 44 | return enrollment is null 45 | ? NotFound() 46 | : Ok(enrollment); 47 | } 48 | 49 | [HttpPost(Name = "Create")] 50 | [ProducesResponseType(typeof(Enrollment), StatusCodes.Status201Created)] 51 | [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] 52 | [ProducesDefaultResponseType] 53 | public async Task Post( 54 | [FromBody] EnrollmentApplicationCommand command, 55 | CancellationToken cancellationToken) 56 | { 57 | var enrollment = await _sender.Send(command, cancellationToken) 58 | .ConfigureAwait(false); 59 | return CreatedAtAction(nameof(GetById), new { id = enrollment.Id }, enrollment); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/Services/Enrolling/Enrolling.API/Enrolling.API.csproj", "src/Services/Enrolling/Enrolling.API/"] 11 | COPY ["src/Services/Enrolling/Enrolling.Infrastructure/Enrolling.Infrastructure.csproj", "src/Services/Enrolling/Enrolling.Infrastructure/"] 12 | COPY ["src/Services/Enrolling/Enrolling.Domain/Enrolling.Domain.csproj", "src/Services/Enrolling/Enrolling.Domain/"] 13 | 14 | COPY ["src/Libraries/OpenTelemetry/OpenTelemetry.csproj", "src/Libraries/OpenTelemetry/"] 15 | 16 | RUN dotnet restore "src/Services/Enrolling/Enrolling.API/Enrolling.API.csproj" 17 | 18 | COPY . . 19 | WORKDIR "/src/src/Services/Enrolling/Enrolling.API" 20 | RUN dotnet build "Enrolling.API.csproj" -c Release -o /app/build 21 | 22 | FROM build as unittest 23 | WORKDIR /src/src/Services/Enrolling/Enrolling.UnitTests 24 | 25 | FROM build as functionaltest 26 | WORKDIR /src/src/Services/Enrolling/Enrolling.FunctionalTests 27 | 28 | FROM build AS publish 29 | RUN dotnet publish "Enrolling.API.csproj" -c Release -o /app/publish 30 | 31 | FROM base AS final 32 | WORKDIR /app 33 | COPY --from=publish /app/publish . 34 | ENTRYPOINT ["dotnet", "Enrolling.API.dll"] -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Enrolling.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | Enrolling.API 5 | OpenCodeFoundation.ESchool.Services.Enrolling.API 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Diagnostics.HealthChecks; 5 | 6 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API.Extensions 7 | { 8 | public static class ServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddCustomHealthChecks( 11 | this IServiceCollection services, 12 | IConfiguration configuration) 13 | { 14 | if (configuration == null) 15 | { 16 | throw new ArgumentNullException(nameof(configuration)); 17 | } 18 | 19 | var hcBuilder = services.AddHealthChecks(); 20 | 21 | hcBuilder 22 | .AddCheck("self", () => HealthCheckResult.Healthy()) 23 | .AddSqlServer( 24 | configuration["ConnectionStrings"], 25 | name: "EnrollingDB-check", 26 | tags: new string[] { "enrollingdb" }); 27 | 28 | return services; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Hosting; 7 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 8 | using Serilog; 9 | using Serilog.Enrichers.Span; 10 | 11 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API 12 | { 13 | public static class Program 14 | { 15 | public static readonly string Namespace = typeof(Program).Namespace!; 16 | public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); 17 | 18 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 19 | "Design", 20 | "CA1031:Do not catch general exception types", 21 | Justification = "Top level all exception catcher")] 22 | public static int Main(string[] args) 23 | { 24 | Activity.DefaultIdFormat = ActivityIdFormat.W3C; 25 | 26 | var configuration = GetConfiguration(); 27 | 28 | Log.Logger = CreateSerilogLogger(configuration); 29 | 30 | try 31 | { 32 | Log.Information("Configuring web host ({ApplicationContext})...", AppName); 33 | var host = CreateHostBuilder(configuration, args).Build(); 34 | 35 | Log.Information("Applying migrations ({ApplicationContext})...", AppName); 36 | host.MigrateDbContext((_, _) => { }); 37 | 38 | Log.Information("Starting web host ({ApplicationContext})...", AppName); 39 | host.Run(); 40 | 41 | return 0; 42 | } 43 | catch (Exception ex) 44 | { 45 | Log.Fatal(ex, "Host terminated unexpectedly"); 46 | return 1; 47 | } 48 | finally 49 | { 50 | Log.CloseAndFlush(); 51 | } 52 | } 53 | 54 | public static IHostBuilder CreateHostBuilder(IConfiguration configuration, string[] args) => 55 | Host.CreateDefaultBuilder(args) 56 | .ConfigureWebHostDefaults(webBuilder => 57 | { 58 | webBuilder.UseStartup(); 59 | webBuilder.UseConfiguration(configuration); 60 | webBuilder.UseSerilog(); 61 | }); 62 | 63 | private static ILogger CreateSerilogLogger(IConfiguration configuration) 64 | { 65 | return new LoggerConfiguration() 66 | .MinimumLevel.Verbose() 67 | .Enrich.WithProperty("ApplicationContext", AppName) 68 | .Enrich.FromLogContext() 69 | .Enrich.WithSpan() 70 | .WriteTo.Console() 71 | .WriteTo.Seq("http://seq") 72 | .ReadFrom.Configuration(configuration) 73 | .CreateLogger(); 74 | } 75 | 76 | private static IConfiguration GetConfiguration() 77 | { 78 | var builder = new ConfigurationBuilder() 79 | .SetBasePath(Directory.GetCurrentDirectory()) 80 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 81 | .AddEnvironmentVariables(); 82 | 83 | // Load other configurations here. Ex. Keyvault or AppConfiguration 84 | return builder.Build(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Text.Json.Serialization; 4 | using FluentValidation.AspNetCore; 5 | using HealthChecks.UI.Client; 6 | using MediatR; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.EntityFrameworkCore; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Hosting; 14 | using Microsoft.Extensions.Logging; 15 | using Microsoft.OpenApi.Models; 16 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Behaviors; 17 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Application.Validations; 18 | using OpenCodeFoundation.ESchool.Services.Enrolling.API.Extensions; 19 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 20 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 21 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Repositories; 22 | using OpenCodeFoundation.OpenTelemetry; 23 | using Serilog; 24 | 25 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API 26 | { 27 | public class Startup 28 | { 29 | public Startup(IConfiguration configuration) 30 | { 31 | Configuration = configuration; 32 | } 33 | 34 | public IConfiguration Configuration { get; } 35 | 36 | // This method gets called by the runtime. Use this method to add services to the container. 37 | public void ConfigureServices(IServiceCollection services) 38 | { 39 | services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly); 40 | 41 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); 42 | 43 | services.AddDbContext(options => 44 | { 45 | options.UseSqlServer( 46 | Configuration["ConnectionStrings"], 47 | sqlServerOptionsAction: sqlOptions => 48 | { 49 | sqlOptions.MigrationsAssembly(typeof(EnrollingContext).GetTypeInfo().Assembly.GetName().Name); 50 | sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); 51 | }); 52 | }); 53 | 54 | services.AddControllers() 55 | .AddJsonOptions(options => 56 | { 57 | options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault; 58 | }) 59 | .AddFluentValidation(fv => 60 | fv.RegisterValidatorsFromAssemblyContaining()); 61 | 62 | services.AddCustomHealthChecks(Configuration); 63 | 64 | services.AddSwaggerGen(c => 65 | { 66 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Enrolling HTTP API", Version = "v1" }); 67 | c.SupportNonNullableReferenceTypes(); 68 | 69 | c.MapType(() => new OpenApiSchema 70 | { 71 | Type = "string", 72 | Format = "uuid", 73 | }); 74 | }); 75 | 76 | services.AddOpenTelemetryIntegration(); 77 | 78 | services.AddTransient(); 79 | } 80 | 81 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 82 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 83 | { 84 | var pathBase = Configuration["PATH_BASE"]; 85 | if (!string.IsNullOrEmpty(pathBase)) 86 | { 87 | loggerFactory.CreateLogger().LogInformation("Using PATH BASE '{pathBase}'", pathBase); 88 | app.UsePathBase(pathBase); 89 | } 90 | 91 | if (env.IsDevelopment()) 92 | { 93 | app.UseDeveloperExceptionPage(); 94 | } 95 | 96 | app.UseSerilogRequestLogging(); 97 | 98 | app.UseSwagger() 99 | .UseSwaggerUI(c => 100 | { 101 | c.SwaggerEndpoint($"{pathBase}/swagger/v1/swagger.json", "Enrolling HTTP API"); 102 | }); 103 | 104 | app.UseRouting(); 105 | 106 | app.UseAuthorization(); 107 | 108 | app.UseEndpoints(endpoints => 109 | { 110 | endpoints.MapControllers(); 111 | 112 | endpoints.MapHealthChecks("/hc", new HealthCheckOptions() 113 | { 114 | Predicate = _ => true, 115 | ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, 116 | }); 117 | endpoints.MapHealthChecks("/liveness", new HealthCheckOptions() 118 | { 119 | Predicate = r => r.Name.Contains("self", StringComparison.Ordinal), 120 | }); 121 | }); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/WebHostExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Data.SqlClient; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Extensions.Logging; 7 | using Polly; 8 | 9 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.API 10 | { 11 | public static class WebHostExtensions 12 | { 13 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 14 | "Design", 15 | "CA1031:Do not catch general exception types", 16 | Justification = "Top level all exception catcher")] 17 | public static IHost MigrateDbContext( 18 | this IHost webHost, 19 | Action seeder) 20 | where TContext : DbContext 21 | { 22 | if (webHost == null) 23 | { 24 | throw new ArgumentNullException(nameof(webHost)); 25 | } 26 | 27 | using var scope = webHost.Services.CreateScope(); 28 | var services = scope.ServiceProvider; 29 | var logger = services.GetRequiredService>(); 30 | var context = services.GetRequiredService(); 31 | 32 | try 33 | { 34 | logger.LogInformation("Migrating database associated with context {ContextName}", typeof(TContext).Name); 35 | 36 | var retry = Policy.Handle() 37 | .WaitAndRetry(new TimeSpan[] 38 | { 39 | TimeSpan.FromSeconds(5), 40 | TimeSpan.FromSeconds(10), 41 | TimeSpan.FromSeconds(15), 42 | }); 43 | 44 | retry.Execute(() => 45 | { 46 | context.Database.Migrate(); 47 | seeder(context, services); 48 | }); 49 | 50 | logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}"); 51 | } 52 | catch (Exception ex) 53 | { 54 | logger.LogError(ex, $"An error occurred while migrating the databases used on context {typeof(TContext).Name}"); 55 | } 56 | 57 | return webHost; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": "Server=tcp:127.0.0.1,5433;Database=OpenCodeFoundation.EnrollingDb;User Id=sa;Password=Pass@word;", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information", 6 | "Override": { 7 | "Microsoft": "Warning", 8 | "OpenCodeFoundation": "Information", 9 | "System": "Warning" 10 | } 11 | } 12 | }, 13 | "AllowedHosts": "*", 14 | "OpenTelemetry": { 15 | "Enabled": true, 16 | "Istio": false, 17 | "Jaeger": { 18 | "Enabled": true, 19 | "ServiceName": "enrolling.api", 20 | "Host": "jaeger", 21 | "Port": 6831 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/AggregatesModel/EnrollmentAggregate/Enrollment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.SeedWork; 3 | 4 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate 5 | { 6 | public class Enrollment 7 | : Entity, IAggregateRoot 8 | { 9 | private Enrollment( 10 | string name, 11 | string emailAddress, 12 | string mobileNumber) 13 | { 14 | Name = !string.IsNullOrWhiteSpace(name) ? name 15 | : throw new ArgumentNullException(nameof(name)); 16 | EmailAddress = !string.IsNullOrWhiteSpace(emailAddress) ? emailAddress 17 | : throw new ArgumentNullException(nameof(emailAddress)); 18 | MobileNumber = !string.IsNullOrWhiteSpace(mobileNumber) ? mobileNumber 19 | : throw new ArgumentNullException(nameof(mobileNumber)); 20 | 21 | Id = EnrollmentId.New(); 22 | } 23 | 24 | public EnrollmentId Id { get; set; } 25 | 26 | public string Name { get; private set; } 27 | 28 | public string EmailAddress { get; private set; } 29 | 30 | public string MobileNumber { get; private set; } 31 | 32 | public static Enrollment CreateNew( 33 | string name, 34 | string emailAddress, 35 | string mobileNumber) 36 | { 37 | return new(name, emailAddress, mobileNumber); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/AggregatesModel/EnrollmentAggregate/EnrollmentId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate 4 | { 5 | [StronglyTypedId(typeof(Guid))] 6 | public partial struct EnrollmentId 7 | { 8 | public override string ToString() 9 | { 10 | return _value.ToString(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/AggregatesModel/EnrollmentAggregate/IEnrollmentRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.SeedWork; 4 | 5 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate 6 | { 7 | public interface IEnrollmentRepository 8 | : IRepository 9 | { 10 | Enrollment Add(Enrollment enrollment); 11 | 12 | Enrollment Update(Enrollment enrollment); 13 | 14 | Task FindByIdAsync( 15 | EnrollmentId id, 16 | CancellationToken cancellationToken = default); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/Enrolling.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | Enrolling.Domain 7 | OpenCodeFoundation.ESchool.Services.Enrolling.Domain 8 | 9 | 10 | True 11 | $(BaseIntermediateOutputPath)\GeneratedFiles 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/SeedWork/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.SeedWork 2 | { 3 | public abstract class Entity 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/SeedWork/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.SeedWork 2 | { 3 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 4 | "Design", 5 | "CA1040:Avoid empty interfaces", 6 | Justification = "Marker interface")] 7 | public interface IAggregateRoot 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Domain/SeedWork/IRepository.cs: -------------------------------------------------------------------------------- 1 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Domain.SeedWork 2 | { 3 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 4 | "Design", 5 | "CA1040:Avoid empty interfaces", 6 | Justification = "Marker interface")] 7 | public interface IRepository 8 | where T : IAggregateRoot 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.FunctionalTests/Enrolling.FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | OpenCodeFoundation.ESchool.Services.Enrolling.FunctionalTests 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Always 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.FunctionalTests/EnrollingTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | 4 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.FunctionalTests 5 | { 6 | [Collection("TestServer")] 7 | public class EnrollingTests 8 | { 9 | private readonly TestServerFixture _testServer; 10 | 11 | public EnrollingTests(TestServerFixture testServer) 12 | { 13 | _testServer = testServer ?? throw new System.ArgumentNullException(nameof(testServer)); 14 | } 15 | 16 | [Fact] 17 | public async Task Get_all_enrolling_ok_status_code() 18 | { 19 | var response = await _testServer.Client.GetAsync("/Enrollments"); 20 | 21 | response.EnsureSuccessStatusCode(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.FunctionalTests/TestServerCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.FunctionalTests 4 | { 5 | [CollectionDefinition("TestServer")] 6 | public class TestServerCollection 7 | : ICollectionFixture 8 | { 9 | // This class has no code, and is never created. Its purpose is simply 10 | // to be the place to apply [CollectionDefinition] and all the 11 | // ICollectionFixture<> interfaces. 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.FunctionalTests/TestServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Http; 4 | using System.Reflection; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.TestHost; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.Hosting; 9 | using OpenCodeFoundation.ESchool.Services.Enrolling.API; 10 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 11 | using Serilog; 12 | 13 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.FunctionalTests 14 | { 15 | public class TestServerFixture 16 | : IDisposable 17 | { 18 | public TestServerFixture() 19 | { 20 | WebHost = CreateHost(); 21 | WebHost.MigrateDbContext((_, __) => { }); 22 | } 23 | 24 | public HttpClient Client => WebHost.GetTestClient(); 25 | 26 | public IHost WebHost { get; } 27 | 28 | public IHost CreateHost() 29 | { 30 | var path = Assembly.GetAssembly(typeof(TestServerFixture)) 31 | .Location; 32 | 33 | var builder = Host.CreateDefaultBuilder() 34 | .UseContentRoot(Path.GetDirectoryName(path)) 35 | .ConfigureWebHostDefaults(webBuilder => 36 | { 37 | webBuilder.UseTestServer(); 38 | webBuilder.UseStartup(); 39 | webBuilder.ConfigureAppConfiguration(config => 40 | { 41 | config.AddJsonFile("appsettings.json", optional: false) 42 | .AddEnvironmentVariables(); 43 | }); 44 | webBuilder.UseSerilog(); 45 | }); 46 | 47 | return builder.Start(); 48 | } 49 | 50 | public void Dispose() 51 | { 52 | WebHost.Dispose(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.FunctionalTests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": "Server=tcp:127.0.0.1,5433;Database=OpenCodeFoundation.EnrollingDb;User Id=sa;Password=Pass@word;" 3 | } -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Configuration/ValueConverters/EnrollmentIdValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 4 | 5 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Configuration.ValueConverters 6 | { 7 | public class EnrollmentIdValueConverter 8 | : ValueConverter 9 | { 10 | public EnrollmentIdValueConverter() 11 | : base(e => e.Value, value => EnrollmentId.FromGuid(value)) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Configuration/ValueConverters/ModelConfigurationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 4 | 5 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Configuration.ValueConverters 6 | { 7 | public static class ModelConfigurationBuilderExtensions 8 | { 9 | public static ModelConfigurationBuilder AddStronglyTypedIds( 10 | this ModelConfigurationBuilder builder) 11 | { 12 | if (builder == null) 13 | { 14 | throw new ArgumentNullException(nameof(builder)); 15 | } 16 | 17 | builder.Properties() 18 | .HaveConversion(typeof(EnrollmentIdValueConverter), null); 19 | return builder; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Enrolling.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | Enrolling.Infrastructure 5 | OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/EnrollingContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 4 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Configuration.ValueConverters; 5 | 6 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure 7 | { 8 | public class EnrollingContext 9 | : DbContext 10 | { 11 | public EnrollingContext(DbContextOptions options) 12 | : base(options) 13 | { 14 | } 15 | 16 | public DbSet Enrollments { get; set; } = default!; 17 | 18 | protected override void ConfigureConventions( 19 | ModelConfigurationBuilder configurationBuilder) 20 | { 21 | configurationBuilder.AddStronglyTypedIds(); 22 | } 23 | } 24 | 25 | /// 26 | /// Helper class for creating migration. To create new migration, run the 27 | /// command from `Enrolling.Infrastructure` folder. 28 | /// 29 | /// $ dotnet ef migrations add name_of_migration --startup-project ../Enrolling.API. 30 | /// 31 | public class EnrollingContextFactory : IDesignTimeDbContextFactory 32 | { 33 | public EnrollingContext CreateDbContext(string[] args) 34 | { 35 | var optionsBuilder = new DbContextOptionsBuilder() 36 | .UseSqlServer("Server=.;Initial Catalog=OpenCodeFoundation.EnrollingDb;Integrated Security=true"); 37 | 38 | return new EnrollingContext(optionsBuilder.Options); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Migrations/20200330154056_Initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 9 | 10 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Migrations 11 | { 12 | [DbContext(typeof(EnrollingContext))] 13 | [Migration("20200330154056_Initial")] 14 | partial class Initial 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "3.1.3") 21 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 23 | 24 | modelBuilder.Entity("OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate.Enrollment", b => 25 | { 26 | b.Property("Id") 27 | .ValueGeneratedOnAdd() 28 | .HasColumnType("uniqueidentifier"); 29 | 30 | b.Property("EmailAddress") 31 | .HasColumnType("nvarchar(max)"); 32 | 33 | b.Property("MobileNumber") 34 | .HasColumnType("nvarchar(max)"); 35 | 36 | b.Property("Name") 37 | .HasColumnType("nvarchar(max)"); 38 | 39 | b.HasKey("Id"); 40 | 41 | b.ToTable("Enrollments"); 42 | }); 43 | #pragma warning restore 612, 618 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Migrations/20200330154056_Initial.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | 5 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Migrations 6 | { 7 | public partial class Initial : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.CreateTable( 12 | name: "Enrollments", 13 | columns: table => new 14 | { 15 | Id = table.Column(nullable: false), 16 | Name = table.Column(nullable: true), 17 | EmailAddress = table.Column(nullable: true), 18 | MobileNumber = table.Column(nullable: true), 19 | }, 20 | constraints: table => 21 | { 22 | table.PrimaryKey("PK_Enrollments", x => x.Id); 23 | }); 24 | } 25 | 26 | protected override void Down(MigrationBuilder migrationBuilder) 27 | { 28 | migrationBuilder.DropTable( 29 | name: "Enrollments"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Migrations/EnrollingContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure; 8 | 9 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Migrations 10 | { 11 | [DbContext(typeof(EnrollingContext))] 12 | partial class EnrollingContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("ProductVersion", "3.1.3") 19 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate.Enrollment", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("uniqueidentifier"); 27 | 28 | b.Property("EmailAddress") 29 | .HasColumnType("nvarchar(max)"); 30 | 31 | b.Property("MobileNumber") 32 | .HasColumnType("nvarchar(max)"); 33 | 34 | b.Property("Name") 35 | .HasColumnType("nvarchar(max)"); 36 | 37 | b.HasKey("Id"); 38 | 39 | b.ToTable("Enrollments"); 40 | }); 41 | #pragma warning restore 612, 618 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.Infrastructure/Repositories/EnrollmentRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 7 | 8 | namespace OpenCodeFoundation.ESchool.Services.Enrolling.Infrastructure.Repositories 9 | { 10 | public class EnrollmentRepository 11 | : IEnrollmentRepository 12 | { 13 | private readonly EnrollingContext _context; 14 | 15 | public EnrollmentRepository(EnrollingContext context) 16 | { 17 | _context = context ?? throw new ArgumentNullException(nameof(context)); 18 | } 19 | 20 | public Enrollment Add(Enrollment enrollment) 21 | { 22 | return _context.Enrollments 23 | .Add(enrollment) 24 | .Entity; 25 | } 26 | 27 | public async Task FindByIdAsync( 28 | EnrollmentId id, 29 | CancellationToken cancellationToken = default) 30 | { 31 | return await _context.Enrollments 32 | .Where(e => e.Id == id) 33 | .SingleOrDefaultAsync(cancellationToken) 34 | .ConfigureAwait(false); 35 | } 36 | 37 | public Enrollment Update(Enrollment enrollment) 38 | { 39 | return _context.Enrollments 40 | .Update(enrollment) 41 | .Entity; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.UnitTests/Builders/EnrollmentDto.cs: -------------------------------------------------------------------------------- 1 | namespace Enrolling.UnitTests.Builders 2 | { 3 | public class EnrollmentDto 4 | { 5 | public string? Name { get; set; } 6 | 7 | public string? Email { get; set; } 8 | 9 | public string? Mobile { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.UnitTests/Builders/EnrollmentDtoBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Enrolling.UnitTests.Builders 2 | { 3 | public class EnrollmentDtoBuilder 4 | { 5 | private string? _name; 6 | private string? _email; 7 | private string? _mobile; 8 | 9 | public EnrollmentDtoBuilder WithDefaults() 10 | { 11 | _name = "John Doe"; 12 | _email = "john@example.com"; 13 | _mobile = "01771999999"; 14 | return this; 15 | } 16 | 17 | public EnrollmentDtoBuilder WithEmptyName() 18 | { 19 | _name = string.Empty; 20 | return this; 21 | } 22 | 23 | public EnrollmentDtoBuilder WithEmail(string email) 24 | { 25 | _email = email; 26 | return this; 27 | } 28 | 29 | public EnrollmentDtoBuilder WithMobile(string mobile) 30 | { 31 | _mobile = mobile; 32 | return this; 33 | } 34 | 35 | public EnrollmentDto Build() 36 | { 37 | return new EnrollmentDto 38 | { 39 | Name = _name, 40 | Email = _email, 41 | Mobile = _mobile, 42 | }; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.UnitTests/Domain/EnrollmentAggregateTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Enrolling.UnitTests.Builders; 3 | using OpenCodeFoundation.ESchool.Services.Enrolling.Domain.AggregatesModel.EnrollmentAggregate; 4 | using Xunit; 5 | 6 | namespace Enrolling.UnitTests.Domain 7 | { 8 | public class EnrollmentAggregateTests 9 | { 10 | [Fact] 11 | public void NewApplicationShouldSuccessWithValidInput() 12 | { 13 | var dto = new EnrollmentDtoBuilder() 14 | .WithDefaults() 15 | .Build(); 16 | 17 | var enrollment = Enrollment.CreateNew(dto.Name!, dto.Email!, dto.Mobile!); 18 | 19 | Assert.NotNull(enrollment); 20 | Assert.Equal(dto.Name, enrollment.Name); 21 | Assert.Equal(dto.Email, enrollment.EmailAddress); 22 | Assert.Equal(dto.Mobile, enrollment.MobileNumber); 23 | } 24 | 25 | [Fact] 26 | public void ShouldThrowExceptionIfNameIsEmpty() 27 | { 28 | var dto = new EnrollmentDtoBuilder() 29 | .WithDefaults() 30 | .WithEmptyName() 31 | .Build(); 32 | 33 | Assert.Throws(() => Enrollment.CreateNew(dto.Name!, dto.Email!, dto.Mobile!)); 34 | } 35 | 36 | [Fact] 37 | public void ShouldThrowExceptionEmptyEmail() 38 | { 39 | var dto = new EnrollmentDtoBuilder() 40 | .WithDefaults() 41 | .WithEmail(string.Empty) 42 | .Build(); 43 | 44 | Assert.Throws(() => Enrollment.CreateNew(dto.Name!, dto.Email!, dto.Mobile!)); 45 | } 46 | 47 | [Fact] 48 | public void ShouldThrowExceptionEmptyMobile() 49 | { 50 | var dto = new EnrollmentDtoBuilder() 51 | .WithDefaults() 52 | .WithMobile(string.Empty) 53 | .Build(); 54 | 55 | Assert.Throws(() => Enrollment.CreateNew(dto.Name!, dto.Email!, dto.Mobile!)); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Services/Enrolling/Enrolling.UnitTests/Enrolling.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/Controllers/HelloWorldController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace ExamManagement.API.Controllers 4 | { 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class HelloWorldController : ControllerBase 8 | { 9 | [HttpGet] 10 | public ActionResult Ping() 11 | { 12 | return Ok("Pong"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/Services/ExamManagement/ExamManagement.API/ExamManagement.API.csproj", "src/Services/ExamManagement/ExamManagement.API/"] 11 | 12 | RUN dotnet restore "src/Services/ExamManagement/ExamManagement.API/ExamManagement.API.csproj" 13 | 14 | COPY . . 15 | WORKDIR "/src/src/Services/ExamManagement/ExamManagement.API" 16 | RUN dotnet build "ExamManagement.API.csproj" -c Release -o /app/build 17 | 18 | FROM build AS publish 19 | RUN dotnet publish "ExamManagement.API.csproj" -c Release -o /app/publish 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | ENTRYPOINT ["dotnet", "ExamManagement.API.dll"] 25 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/ExamManagement.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Hosting; 7 | using Serilog; 8 | using Serilog.Enrichers.Span; 9 | 10 | namespace ExamManagement.API 11 | { 12 | public static class Program 13 | { 14 | public static readonly string Namespace = typeof(Program).Namespace!; 15 | public static readonly string AppName = Namespace[(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1)..]; 16 | 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 18 | "Design", 19 | "CA1031:Do not catch general exception types", 20 | Justification = "Top level all exception catcher")] 21 | public static int Main(string[] args) 22 | { 23 | Activity.DefaultIdFormat = ActivityIdFormat.W3C; 24 | 25 | var configuration = GetConfiguration(); 26 | 27 | Log.Logger = CreateSerilogLogger(configuration); 28 | try 29 | { 30 | Log.Information("Configuring web host ({ApplicationContext})...", AppName); 31 | CreateHostBuilder(configuration, args).Build().Run(); 32 | 33 | return 0; 34 | } 35 | catch (Exception ex) 36 | { 37 | Log.Fatal(ex, "Host terminated unexpectedly"); 38 | return 1; 39 | } 40 | finally 41 | { 42 | Log.CloseAndFlush(); 43 | } 44 | } 45 | 46 | public static IHostBuilder CreateHostBuilder(IConfiguration configuration, string[] args) => 47 | Host.CreateDefaultBuilder(args) 48 | .ConfigureWebHostDefaults(webBuilder => 49 | { 50 | webBuilder.UseStartup(); 51 | webBuilder.UseConfiguration(configuration); 52 | webBuilder.UseSerilog(); 53 | }); 54 | 55 | private static ILogger CreateSerilogLogger(IConfiguration configuration) 56 | { 57 | return new LoggerConfiguration() 58 | .MinimumLevel.Verbose() 59 | .Enrich.WithProperty("ApplicationContext", AppName) 60 | .Enrich.FromLogContext() 61 | .Enrich.WithSpan() 62 | .WriteTo.Console() 63 | .WriteTo.Seq("http://seq") 64 | .ReadFrom.Configuration(configuration) 65 | .CreateLogger(); 66 | } 67 | 68 | private static IConfiguration GetConfiguration() 69 | { 70 | var builder = new ConfigurationBuilder() 71 | .SetBasePath(Directory.GetCurrentDirectory()) 72 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 73 | .AddEnvironmentVariables(); 74 | 75 | return builder.Build(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.OpenApi.Models; 8 | using Serilog; 9 | 10 | namespace ExamManagement.API 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers(); 25 | services.AddSwaggerGen(c => 26 | { 27 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "ExamManagement.API", Version = "v1" }); 28 | }); 29 | } 30 | 31 | public void Configure( 32 | IApplicationBuilder app, 33 | IWebHostEnvironment env, 34 | ILoggerFactory loggerFactory) 35 | { 36 | var pathBase = Configuration["PATH_BASE"]; 37 | if (!string.IsNullOrEmpty(pathBase)) 38 | { 39 | loggerFactory.CreateLogger().LogInformation("Using PATH BASE '{pathBase}'", pathBase); 40 | app.UsePathBase(pathBase); 41 | } 42 | 43 | if (env.IsDevelopment()) 44 | { 45 | app.UseDeveloperExceptionPage(); 46 | } 47 | 48 | app.UseSerilogRequestLogging(); 49 | 50 | app.UseSwagger(); 51 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ExamManagement.API v1")); 52 | 53 | app.UseRouting(); 54 | 55 | app.UseAuthorization(); 56 | 57 | app.UseEndpoints(endpoints => 58 | { 59 | endpoints.MapControllers(); 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/ExamManagement/ExamManagement.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/Controllers/HelloWorldController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace LibraryManagement.API.Controllers 4 | { 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class HelloWorldController : ControllerBase 8 | { 9 | [HttpGet] 10 | public ActionResult Ping() 11 | { 12 | return Ok("Pong"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["src/Services/LibraryManagement/LibraryManagement.API/LibraryManagement.API.csproj", "src/Services/LibraryManagement/LibraryManagement.API/"] 10 | 11 | RUN dotnet restore "src/Services/LibraryManagement/LibraryManagement.API/LibraryManagement.API.csproj" 12 | 13 | COPY . . 14 | WORKDIR "/src/src/Services/LibraryManagement/LibraryManagement.API" 15 | RUN dotnet build "LibraryManagement.API.csproj" -c Release -o /app/build 16 | 17 | FROM build AS publish 18 | RUN dotnet publish "LibraryManagement.API.csproj" -c Release -o /app/publish 19 | 20 | FROM base AS final 21 | WORKDIR /app 22 | COPY --from=publish /app/publish . 23 | ENTRYPOINT ["dotnet", "LibraryManagement.API.dll"] 24 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/LibraryManagement.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace LibraryManagement.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.HttpsPolicy; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Hosting; 12 | using Microsoft.Extensions.Logging; 13 | using Microsoft.OpenApi.Models; 14 | 15 | namespace LibraryManagement.API 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | 30 | services.AddControllers(); 31 | services.AddSwaggerGen(c => 32 | { 33 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "LibraryManagement.API", Version = "v1" }); 34 | }); 35 | } 36 | 37 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 38 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 39 | { 40 | if (env.IsDevelopment()) 41 | { 42 | app.UseDeveloperExceptionPage(); 43 | } 44 | app.UseSwagger(); 45 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LibraryManagement.API v1")); 46 | 47 | app.UseRouting(); 48 | 49 | app.UseAuthorization(); 50 | 51 | app.UseEndpoints(endpoints => 52 | { 53 | endpoints.MapControllers(); 54 | }); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/LibraryManagement/LibraryManagement.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/Controllers/HelloWorldController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace ResultProcessing.API.Controllers 4 | { 5 | [Route("api/[controller]")] 6 | [ApiController] 7 | public class HelloWorldController : ControllerBase 8 | { 9 | [HttpGet] 10 | public ActionResult Ping() 11 | { 12 | return Ok("This is new Web API"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["src/Services/ResultProcessing/ResultProcessing.API/ResultProcessing.API.csproj", "src/Services/ResultProcessing/ResultProcessing.API/"] 10 | 11 | RUN dotnet restore "src/Services/ResultProcessing/ResultProcessing.API/ResultProcessing.API.csproj" 12 | 13 | COPY . . 14 | WORKDIR "/src/src/Services/ResultProcessing/ResultProcessing.API" 15 | RUN dotnet build "ResultProcessing.API.csproj" -c Release -o /app/build 16 | 17 | FROM build AS publish 18 | RUN dotnet publish "ResultProcessing.API.csproj" -c Release -o /app/publish 19 | 20 | FROM base AS final 21 | WORKDIR /app 22 | COPY --from=publish /app/publish . 23 | ENTRYPOINT ["dotnet", "ResultProcessing.API.dll"] 24 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ResultProcessing.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/ResultProcessing.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.OpenApi.Models; 7 | 8 | namespace ResultProcessing.API 9 | { 10 | public class Startup 11 | { 12 | public Startup(IConfiguration configuration) 13 | { 14 | Configuration = configuration; 15 | } 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | 23 | services.AddControllers(); 24 | services.AddSwaggerGen(c => 25 | { 26 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "ResultProcessing.API", Version = "v1" }); 27 | }); 28 | } 29 | 30 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 32 | { 33 | if (env.IsDevelopment()) 34 | { 35 | app.UseDeveloperExceptionPage(); 36 | } 37 | app.UseSwagger(); 38 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ResultProcessing.API v1")); 39 | 40 | app.UseRouting(); 41 | 42 | app.UseAuthorization(); 43 | 44 | app.UseEndpoints(endpoints => 45 | { 46 | endpoints.MapControllers(); 47 | }); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/ResultProcessing/ResultProcessing.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "strawberryshake.tools": { 6 | "version": "11.0.0-preview.138", 7 | "commands": [ 8 | "dotnet-graphql" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "strawberryshake.tools": { 6 | "version": "11.3.2", 7 | "commands": [ 8 | "dotnet-graphql" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/.graphqlrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": "schema.graphql", 3 | "documents": "**/*.graphql", 4 | "extensions": { 5 | "strawberryShake": { 6 | "name": "EschoolClient", 7 | "namespace": "OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Client", 8 | "url": "http://localhost:5101/graphql/", 9 | "dependencyInjection": true, 10 | "strictSchemaValidation": true, 11 | "hashAlgorithm": "md5", 12 | "useSingleFile": true, 13 | "requestStrategy": "Default", 14 | "outputDirectoryName": "Generated", 15 | "noStore": false, 16 | "emitGeneratedCode": false, 17 | "razorComponents": false, 18 | "records": { 19 | "inputs": true, 20 | "entities": true 21 | }, 22 | "transportProfiles": [ 23 | { 24 | "default": "Http", 25 | "subscription": "WebSocket" 26 | } 27 | ] 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/EnrollmentQuery.graphql: -------------------------------------------------------------------------------- 1 | query getEnrollments { 2 | enrollments { 3 | ... enrollment 4 | } 5 | } 6 | 7 | mutation registerStudent($fullName: String! $email: String! $mobile: String!) { 8 | createEnrollment(enrollment: {name: $fullName, email: $email mobile: $mobile}) { 9 | ... enrollmentId 10 | } 11 | } 12 | 13 | fragment enrollment on Enrollment { 14 | ... enrollmentId 15 | name 16 | emailAddress 17 | mobileNumber 18 | } 19 | 20 | fragment enrollmentId on Enrollment { 21 | id 22 | } 23 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Frontend.Blazor.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | service-worker-assets.js 5 | enable 6 | Frontend.Blazor.Client 7 | OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Client 8 | True 9 | $(BaseIntermediateOutputPath)\GeneratedFiles 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | 4 | Counter 5 | Current count: @currentCount 6 | Click me 7 | 8 | 9 | @code { 10 | private int currentCount = 0; 11 | 12 | private void IncrementCount() 13 | { 14 | currentCount++; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Pages/Enrollments/CreateEnrollment.razor: -------------------------------------------------------------------------------- 1 | @page "/enrollments/create" 2 | @using System.ComponentModel.DataAnnotations 3 | @using StrawberryShake 4 | 5 | @inject IEschoolClient EschoolClient 6 | @inject NavigationManager NavigationManager 7 | @inject ISnackbar Snackbar 8 | 9 | 10 | 11 | 12 | 17 | 23 | 27 | 28 | 29 | Create 34 | 35 | 36 | 37 | 38 | @code { 39 | bool Success; 40 | 41 | string _fullName; 42 | string _email; 43 | string _mobile; 44 | 45 | private async Task Callback(MouseEventArgs obj) 46 | { 47 | 48 | var result = await EschoolClient 49 | .RegisterStudent.ExecuteAsync(_fullName, _email, _mobile); 50 | 51 | if (result.IsErrorResult()) 52 | { 53 | foreach (var error in result.Errors) 54 | { 55 | Snackbar.Add(error.Message, Severity.Error); 56 | } 57 | 58 | return; 59 | } 60 | 61 | NavigationManager.NavigateTo("enrollments"); 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Pages/Enrollments/Enrollments.razor: -------------------------------------------------------------------------------- 1 | @page "/enrollments" 2 | @using StrawberryShake 3 | 4 | @inject IEschoolClient ESchoolClient 5 | @inject ISnackbar Snackbar 6 | 7 | 8 | Enrollment Lists 9 | Get enrollment from database 10 | @_selectedName 11 | @if (_enrollments == null) 12 | { 13 | 14 | } 15 | else 16 | { 17 | 19 | 20 | 21 | Name 23 | 24 | 25 | Mobile 26 | 27 | 28 | Email 29 | 30 | 31 | 32 | @context.Name 33 | @context.MobileNumber 34 | @context.EmailAddress 35 | 36 | 37 | 38 | 39 | 40 | } 41 | 42 | 43 | @code { 44 | private IReadOnlyList? _enrollments; 45 | 46 | private string _selectedName = string.Empty; 47 | 48 | protected override async Task OnInitializedAsync() 49 | { 50 | var result = await ESchoolClient.GetEnrollments.ExecuteAsync(); 51 | 52 | if (result.IsErrorResult()) 53 | { 54 | foreach (var error in result.Errors) 55 | { 56 | Snackbar.Add(error.Message, Severity.Error); 57 | } 58 | } 59 | else 60 | { 61 | _enrollments = result.Data?.Enrollments; 62 | } 63 | 64 | } 65 | 66 | private Task OnItemClicked(IEnrollment enrollment) 67 | { 68 | _selectedName = enrollment.Name; 69 | return Task.CompletedTask; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | 4 | Hello, world! 5 | Welcome to your new app, powerd by MudBlazor! 6 | You can find documentation and examples on our website here: www.mudblazor.com 7 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Net.Http.Json; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using MudBlazor; 8 | using MudBlazor.Services; 9 | using OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Shared; 10 | 11 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Client 12 | { 13 | public static class Program 14 | { 15 | public static async Task Main(string[] args) 16 | { 17 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 18 | builder.RootComponents.Add("#app"); 19 | 20 | var settings = await LoadFrontendSettings(builder) 21 | .ConfigureAwait(false); 22 | builder.Services.AddSingleton(settings); 23 | 24 | builder.Services 25 | .AddEschoolClient() 26 | .ConfigureHttpClient(client => client.BaseAddress = new Uri(settings.GraphQlGatewayEndpoint)); 27 | 28 | builder.Services.AddScoped(_ => 29 | new HttpClient 30 | { 31 | BaseAddress = new Uri(builder.HostEnvironment.BaseAddress), 32 | }); 33 | 34 | builder.Services.AddMudServices(config => 35 | { 36 | config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight; 37 | config.SnackbarConfiguration.SnackbarVariant = Variant.Filled; 38 | }); 39 | 40 | await builder.Build().RunAsync().ConfigureAwait(false); 41 | } 42 | 43 | private static async Task LoadFrontendSettings(WebAssemblyHostBuilder builder) 44 | { 45 | using var http = new HttpClient() 46 | { 47 | BaseAddress = new Uri(builder.HostEnvironment.BaseAddress), 48 | }; 49 | 50 | var settings = await http 51 | .GetFromJsonAsync("appsettings") 52 | .ConfigureAwait(false); 53 | 54 | return settings ?? throw new InvalidOperationException( 55 | "Failed to load settings"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | eSchool 17 | 18 | 19 | 20 | 21 | @Body 22 | 23 | 24 | 25 | @code { 26 | bool _drawerOpen = true; 27 | 28 | void DrawerToggle() 29 | { 30 | _drawerOpen = !_drawerOpen; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  2 | Home 3 | Counter 4 | 5 | List 9 | Create 13 | 14 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Client 10 | @using OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Client.Shared 11 | @using MudBlazor 12 | @using MudBlazor.Dialog 13 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/schema.extensions.graphql: -------------------------------------------------------------------------------- 1 | scalar _KeyFieldSet 2 | 3 | directive @key(fields: _KeyFieldSet!) on SCHEMA | OBJECT 4 | 5 | directive @serializationType(name: String!) on SCALAR 6 | 7 | directive @runtimeType(name: String!) on SCALAR 8 | 9 | directive @enumValue(value: String!) on ENUM_VALUE 10 | 11 | directive @rename(name: String!) on INPUT_FIELD_DEFINITION | INPUT_OBJECT | ENUM | ENUM_VALUE 12 | 13 | extend schema @key(fields: "id") -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/schema.graphql: -------------------------------------------------------------------------------- 1 | schema { 2 | query: Query 3 | mutation: Mutation 4 | } 5 | 6 | type Query { 7 | enrollments: [Enrollment!]! 8 | enrollment(enrollmentId: Uuid!): Enrollment! 9 | } 10 | 11 | type Mutation { 12 | createEnrollment(enrollment: EnrollmentApplicationCommandInput!): Enrollment! 13 | } 14 | 15 | scalar Uuid 16 | 17 | type Enrollment { 18 | id: Uuid! 19 | name: String! 20 | emailAddress: String! 21 | mobileNumber: String! 22 | } 23 | 24 | input EnrollmentApplicationCommandInput { 25 | name: String! 26 | email: String! 27 | mobile: String! 28 | } -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenCodeFoundation/eSchool/46b19b553096c605ac96194f4e5a3b022ae9cf6b/src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenCodeFoundation/eSchool/46b19b553096c605ac96194f4e5a3b022ae9cf6b/src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/icon-512.png -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | eSchool 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Loading...
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eSchool", 3 | "short_name": "eSchool", 4 | "start_url": "./", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#03173d", 8 | "icons": [ 9 | { 10 | "src": "icon-512.png", 11 | "type": "image/png", 12 | "sizes": "512x512" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/service-worker.js: -------------------------------------------------------------------------------- 1 | // In development, always fetch from the network and do not enable offline support. 2 | // This is because caching would make development more difficult (changes would not 3 | // be reflected on the first load after each change). 4 | self.addEventListener('fetch', () => { }); 5 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Client/wwwroot/service-worker.published.js: -------------------------------------------------------------------------------- 1 | // Caution! Be sure you understand the caveats before publishing an application with 2 | // offline support. See https://aka.ms/blazor-offline-considerations 3 | 4 | self.importScripts('./service-worker-assets.js'); 5 | self.addEventListener('install', event => event.waitUntil(onInstall(event))); 6 | self.addEventListener('activate', event => event.waitUntil(onActivate(event))); 7 | self.addEventListener('fetch', event => event.respondWith(onFetch(event))); 8 | 9 | const cacheNamePrefix = 'offline-cache-'; 10 | const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; 11 | const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; 12 | const offlineAssetsExclude = [ /^service-worker\.js$/ ]; 13 | 14 | async function onInstall(event) { 15 | console.info('Service worker: Install'); 16 | 17 | // Fetch and cache all matching items from the assets manifest 18 | const assetsRequests = self.assetsManifest.assets 19 | .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) 20 | .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) 21 | .map(asset => new Request(asset.url, { integrity: asset.hash })); 22 | await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); 23 | } 24 | 25 | async function onActivate(event) { 26 | console.info('Service worker: Activate'); 27 | 28 | // Delete unused caches 29 | const cacheKeys = await caches.keys(); 30 | await Promise.all(cacheKeys 31 | .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) 32 | .map(key => caches.delete(key))); 33 | } 34 | 35 | async function onFetch(event) { 36 | let cachedResponse = null; 37 | if (event.request.method === 'GET') { 38 | // For all navigation requests, try to serve index.html from cache 39 | // If you need some URLs to be server-rendered, edit the following check to exclude those URLs 40 | const shouldServeIndexHtml = event.request.mode === 'navigate'; 41 | 42 | const request = shouldServeIndexHtml ? 'index.html' : event.request; 43 | const cache = await caches.open(cacheName); 44 | cachedResponse = await cache.match(request); 45 | } 46 | 47 | return cachedResponse || fetch(event.request); 48 | } 49 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Controllers/AppSettingsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Options; 4 | using OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Shared; 5 | 6 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server.Controllers 7 | { 8 | [ApiController] 9 | [Route("[controller]")] 10 | public class AppSettingsController 11 | : ControllerBase 12 | { 13 | private readonly FrontendSettings _settings; 14 | 15 | public AppSettingsController(IOptions options) 16 | { 17 | if (options == null) 18 | { 19 | throw new ArgumentNullException(nameof(options)); 20 | } 21 | 22 | _settings = options.Value; 23 | } 24 | 25 | // GET 26 | public FrontendSettings Index() 27 | { 28 | return _settings; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/Web/Frontend.Blazor/Frontend.Blazor.Server/Frontend.Blazor.Server.csproj", "src/Web/Frontend.Blazor/Frontend.Blazor.Server/"] 11 | COPY ["src/Web/Frontend.Blazor/Frontend.Blazor.Shared/Frontend.Blazor.Shared.csproj", "src/Web/Frontend.Blazor/Frontend.Blazor.Shared/"] 12 | COPY ["src/Web/Frontend.Blazor/Frontend.Blazor.Client/Frontend.Blazor.Client.csproj", "src/Web/Frontend.Blazor/Frontend.Blazor.Client/"] 13 | 14 | RUN dotnet restore "src/Web/Frontend.Blazor/Frontend.Blazor.Server/Frontend.Blazor.Server.csproj" 15 | 16 | COPY . . 17 | WORKDIR "/src/src/Web/Frontend.Blazor/Frontend.Blazor.Server" 18 | RUN dotnet build "Frontend.Blazor.Server.csproj" -c Release -o /app/build 19 | 20 | FROM build AS publish 21 | RUN dotnet publish "Frontend.Blazor.Server.csproj" -c Release -o /app/publish 22 | 23 | FROM base AS final 24 | WORKDIR /app 25 | COPY --from=publish /app/publish . 26 | ENTRYPOINT ["dotnet", "Frontend.Blazor.Server.dll"] -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Frontend.Blazor.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | enable 5 | Frontend.Blazor.Server 6 | OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Error.

19 |

An error occurred while processing your request.

20 | 21 | @if (Model.ShowRequestId) 22 | { 23 |

24 | Request ID: @Model.RequestId 25 |

26 | } 27 | 28 |

Development Mode

29 |

30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |

32 |

33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |

38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | [IgnoreAntiforgeryToken] 14 | public class ErrorModel : PageModel 15 | { 16 | public string RequestId { get; set; } 17 | 18 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 19 | 20 | private readonly ILogger _logger; 21 | 22 | public ErrorModel(ILogger logger) 23 | { 24 | _logger = logger; 25 | } 26 | 27 | public void OnGet() 28 | { 29 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Shared; 7 | 8 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Server 9 | { 10 | public class Startup 11 | { 12 | public Startup(IConfiguration configuration) 13 | { 14 | Configuration = configuration; 15 | } 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration); 24 | 25 | services.AddControllersWithViews(); 26 | services.AddRazorPages(); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 30 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 31 | { 32 | if (env.IsDevelopment()) 33 | { 34 | app.UseDeveloperExceptionPage(); 35 | app.UseWebAssemblyDebugging(); 36 | } 37 | else 38 | { 39 | app.UseExceptionHandler("/Error"); 40 | } 41 | 42 | app.UseBlazorFrameworkFiles(); 43 | app.UseStaticFiles(); 44 | 45 | app.UseRouting(); 46 | 47 | app.UseEndpoints(endpoints => 48 | { 49 | endpoints.MapRazorPages(); 50 | endpoints.MapControllers(); 51 | endpoints.MapFallbackToFile("index.html"); 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "GraphQlGatewayEndpoint": "http://localhost:5101/graphql" 11 | } 12 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Shared/Frontend.Blazor.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | enable 7 | Frontend.Blazor.Shared 8 | OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Shared 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/Frontend.Blazor.Shared/FrontendSettings.cs: -------------------------------------------------------------------------------- 1 | namespace OpenCodeFoundation.ESchool.Web.Frontend.Blazor.Shared 2 | { 3 | public class FrontendSettings 4 | { 5 | public string? GraphQlGatewayEndpoint { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Web/Frontend.Blazor/README.md: -------------------------------------------------------------------------------- 1 | initialize local tool 2 | 3 | ```bash 4 | dotnet new tool-manifest 5 | ``` 6 | 7 | Install local tool 8 | 9 | ```bash 10 | dotnet tool restore 11 | ``` 12 | 13 | init graphql config 14 | 15 | ```bash 16 | dotnet graphql init 17 | ``` -------------------------------------------------------------------------------- /src/Web/WebStatus/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | 10 | COPY ["src/Web/WebStatus/WebStatus.csproj", "src/Web/WebStatus/"] 11 | 12 | RUN dotnet restore "src/Web/WebStatus/WebStatus.csproj" 13 | 14 | COPY . . 15 | WORKDIR "/src/src/Web/WebStatus" 16 | RUN dotnet build "WebStatus.csproj" -c Release -o /app/build 17 | 18 | FROM build AS publish 19 | RUN dotnet publish "WebStatus.csproj" -c Release -o /app/publish 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | ENTRYPOINT ["dotnet", "WebStatus.dll"] -------------------------------------------------------------------------------- /src/Web/WebStatus/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace OpenCodeFoundation.ESchool.Web.WebStatus 6 | { 7 | public static class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | Activity.DefaultIdFormat = ActivityIdFormat.W3C; 12 | 13 | CreateHostBuilder(args).Build().Run(); 14 | } 15 | 16 | public static IHostBuilder CreateHostBuilder(string[] args) => 17 | Host.CreateDefaultBuilder(args) 18 | .ConfigureWebHostDefaults(webBuilder => 19 | { 20 | webBuilder.UseStartup(); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Web/WebStatus/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace OpenCodeFoundation.ESchool.Web.WebStatus 8 | { 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services 22 | .AddHealthChecksUI() 23 | .AddSqlServerStorage(Configuration["ConnectionStrings"]); 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 27 | [System.Diagnostics.CodeAnalysis.SuppressMessage( 28 | "Performance", 29 | "CA1822:Mark members as static", 30 | Justification = "This method is called from framework")] 31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 32 | { 33 | if (env.IsDevelopment()) 34 | { 35 | app.UseDeveloperExceptionPage(); 36 | } 37 | 38 | app.UseRouting(); 39 | 40 | app.UseEndpoints(endpoints => 41 | { 42 | endpoints.MapHealthChecksUI(setup => 43 | setup.UIPath = "/"); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Web/WebStatus/WebStatus.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0 5 | WebStatus 6 | OpenCodeFoundation.ESchool.Web.WebStatus 7 | 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Web/WebStatus/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Web/WebStatus/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "HealthChecksUI": { 3 | "HealthChecks": [ 4 | { 5 | "Name": "HTTP-Api-Basic", 6 | "Uri": "http://localhost:6457/healthz" 7 | } 8 | ], 9 | "Webhooks": [ 10 | { 11 | "Name": "", 12 | "Uri": "", 13 | "Payload": "", 14 | "RestoredPayload": "" 15 | } 16 | ], 17 | "EvaluationTimeInSeconds": 10, 18 | "MinimumSecondsBetweenFailureNotifications": 60 19 | }, 20 | "Logging": { 21 | "LogLevel": { 22 | "Default": "Information", 23 | "Microsoft": "Warning", 24 | "Microsoft.Hosting.Lifetime": "Information" 25 | } 26 | }, 27 | "AllowedHosts": "*" 28 | } --------------------------------------------------------------------------------