diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..e404a4b --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,17 @@ +version = 1 + +[[analyzers]] +name = "csharp" + +[[analyzers]] +name = "shell" + +[[analyzers]] +name = "javascript" + +[[analyzers]] +name = "test-coverage" +enabled = true + +[[transformers]] +name = "dotnet-format" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..8c88068 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Belirli bir dosyayı kullanıcı veya takıma atamak için: +* @middt @safakcakir diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..f50c0a7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,14 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +## Describe the bug + + +## Platform + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..0647aa9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEA]" +labels: enhancement, need-documentation +assignees: '' + +--- + +## Purpose + + +## Solution + + +## Release Note + + + + + + + +RELEASE NOTE: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..160e579 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +#### Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Fixes # (issue) + +#### Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +#### How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration + +#### Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66c8b39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,357 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +#Adding jetbrains idea folder +*.idea + +# Ignore test results +tests/collections/newman +tests/collections/reports diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..88dde6e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,88 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "amorphie-template", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "dapr-debug-amorphie-template", + "program": "${workspaceFolder}/amorphie.template/bin/Debug/net7.0/amorphie.template.dll", + "args": [], + "cwd": "${workspaceFolder}/amorphie.template", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:4200", + "DAPR_HTTP_PORT": "42010", + "DAPR_GRPC_PORT": "42011", + "DAPR_SECRET_STORE_NAME": "transaction-secretstore", + "DAPR_STATE_STORE_NAME": "transaction-cache" + }, + "postDebugTask": "daprd-down-amorphie-template", + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)", + "uriFormat": "%s/swagger/" + } + }, + { + "name": "amorphie-template-hub", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "dapr-debug-amorphie-template-hub", + "program": "${workspaceFolder}/amorphie.template.hub/bin/Debug/net7.0/amorphie.template.hub.dll", + "args": [], + "cwd": "${workspaceFolder}/amorphie.template.hub", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:4201", + "DAPR_HTTP_PORT": "42020", + "DAPR_GRPC_PORT": "42021", + "DAPR_SECRET_STORE_NAME": "transaction-secretstore", + "DAPR_STATE_STORE_NAME": "transaction-cache" + }, + "postDebugTask": "daprd-down-amorphie-template-hub", + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)", + "uriFormat": "%s/swagger/" + } + }, + { + "name": "amorphie-template-worker", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "dapr-debug-amorphie-template-worker", + "program": "${workspaceFolder}/amorphie.template.worker/bin/Debug/net7.0/amorphie.template.worker.dll", + "args": [], + "cwd": "${workspaceFolder}/amorphie.template.worker", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:4202", + "DAPR_HTTP_PORT": "42030", + "DAPR_GRPC_PORT": "42031", + "DAPR_SECRET_STORE_NAME": "transaction-secretstore", + "DAPR_STATE_STORE_NAME": "transaction-cache" + }, + "postDebugTask": "daprd-down-amorphie-template-worker", + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)", + "uriFormat": "%s/swagger/" + } + } + ], + "compounds": [ + { + "name": "Halay Mode", + "configurations": [ + "amorphie-template", + "amorphie-template-hub", + "amorphie-template-worker" + ], + "stopAll": true + } + ], +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..12930f1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "template.sln" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e5c60d7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,88 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build-amorphie-template", + "dependsOn": "init-mocks", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/amorphie.template/amorphie.template.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "build-amorphie-template-hub", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/amorphie.template.hub/amorphie.template.hub.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "build-amorphie-template-worker", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/amorphie.template.worker/amorphie.template.worker.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "appId": "amorphie-template", + "appPort": 4200, + "httpPort": 42010, + "grpcPort": 42011, + "label": "dapr-debug-amorphie-template", + "type": "dapr", + "dependsOn": "build-amorphie-template", + "componentsPath": "Dapr/Components" + }, + { + "appId": "amorphie-template-hub", + "appPort": 4201, + "httpPort": 42020, + "grpcPort": 42021, + "label": "dapr-debug-amorphie-template-hub", + "type": "dapr", + "dependsOn": "build-amorphie-template-hub", + "componentsPath": "Dapr/Components", + }, + { + "appId": "amorphie-template-worker", + "appPort": 4202, + "httpPort": 42030, + "grpcPort": 42031, + "label": "dapr-debug-amorphie-template-worker", + "type": "dapr", + "dependsOn": "build-amorphie-template-worker", + "componentsPath": "Dapr/Components" + }, + { + "appId": "amorphie-template", + "label": "daprd-down-amorphie-template", + "type": "daprd-down", + "dependsOn": "stop-mocks", + }, + { + "appId": "amorphie-template-hub", + "label": "daprd-down-amorphie-template-hub", + "type": "daprd-down" + }, + { + "appId": "amorphie-template-worker", + "label": "daprd-down-amorphie-template-worker", + "type": "daprd-down" + } + ] +} diff --git a/amorphie.template.core/DTO/StudentDTO.cs b/amorphie.template.core/DTO/StudentDTO.cs new file mode 100644 index 0000000..926dabc --- /dev/null +++ b/amorphie.template.core/DTO/StudentDTO.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.core.Base; + +namespace amorphie.template.core.Model; + +public class StudentDTO : DtoBase +{ + public string LastName { get; set; } + public string FirstMidName { get; set; } + public DateTime EnrollmentDate { get; set; } +} diff --git a/amorphie.template.core/Model/Course.cs b/amorphie.template.core/Model/Course.cs new file mode 100644 index 0000000..7f0f345 --- /dev/null +++ b/amorphie.template.core/Model/Course.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.core.Base; + +namespace amorphie.template.core.Model; + +public class Course : EntityBase +{ + public string Title { get; set; } + public int Credits { get; set; } + + public ICollection Enrollments { get; set; } +} diff --git a/amorphie.template.core/Model/Enrollment.cs b/amorphie.template.core/Model/Enrollment.cs new file mode 100644 index 0000000..414bf55 --- /dev/null +++ b/amorphie.template.core/Model/Enrollment.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.core.Base; + +namespace amorphie.template.core.Model; + +public enum Grade +{ + A, + B, + C, + D, + F +} + +public class Enrollment : EntityBase +{ + public Grade? Grade { get; set; } + + public Course Course { get; set; } + public Student Student { get; set; } +} diff --git a/amorphie.template.core/Model/Student.cs b/amorphie.template.core/Model/Student.cs new file mode 100644 index 0000000..c6e232b --- /dev/null +++ b/amorphie.template.core/Model/Student.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.core.Base; + +namespace amorphie.template.core.Model; + +public class Student : EntityBase +{ + public string LastName { get; set; } + public string FirstMidName { get; set; } + public DateTime EnrollmentDate { get; set; } + + public ICollection Enrollments { get; set; } +} diff --git a/amorphie.template.core/Search/StudentSearch.cs b/amorphie.template.core/Search/StudentSearch.cs new file mode 100644 index 0000000..81305a3 --- /dev/null +++ b/amorphie.template.core/Search/StudentSearch.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.core.Base; + +namespace amorphie.template.core.Search; + +public class StudentSearch : DtoSearchBase { } diff --git a/amorphie.template.core/amorphie.template.core.csproj b/amorphie.template.core/amorphie.template.core.csproj new file mode 100644 index 0000000..55d3d6e --- /dev/null +++ b/amorphie.template.core/amorphie.template.core.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/amorphie.template.data/DbInitializer.cs b/amorphie.template.data/DbInitializer.cs new file mode 100644 index 0000000..0ae891e --- /dev/null +++ b/amorphie.template.data/DbInitializer.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.template.core.Model; + +namespace amorphie.template.data; + +public static class DbInitializer +{ + public static void Initialize(TemplateDbContext context) + { + context.Database.EnsureCreated(); + + // Look for any students. + if (context.Students.Any()) + { + return; // DB has been seeded + } + + var students = new Student[] + { + new Student + { + FirstMidName = "Carson", + LastName = "Alexander", + EnrollmentDate = DateTime.Parse("2005-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Meredith", + LastName = "Alonso", + EnrollmentDate = DateTime.Parse("2002-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Arturo", + LastName = "Anand", + EnrollmentDate = DateTime.Parse("2003-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Gytis", + LastName = "Barzdukas", + EnrollmentDate = DateTime.Parse("2002-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Yan", + LastName = "Li", + EnrollmentDate = DateTime.Parse("2002-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Peggy", + LastName = "Justice", + EnrollmentDate = DateTime.Parse("2001-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Laura", + LastName = "Norman", + EnrollmentDate = DateTime.Parse("2003-09-01").ToUniversalTime() + }, + new Student + { + FirstMidName = "Nino", + LastName = "Olivetto", + EnrollmentDate = DateTime.Parse("2005-09-01").ToUniversalTime() + } + }; + foreach (Student s in students) + { + context.Students.Add(s); + } + context.SaveChanges(); + + var courses = new Course[] + { + new Course + { + Id = Guid.NewGuid(), + Title = "Chemistry", + Credits = 3 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Microeconomics", + Credits = 3 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Macroeconomics", + Credits = 3 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Calculus", + Credits = 4 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Trigonometry", + Credits = 4 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Composition", + Credits = 3 + }, + new Course + { + Id = Guid.NewGuid(), + Title = "Literature", + Credits = 4 + } + }; + foreach (Course c in courses) + { + context.Courses.Add(c); + } + context.SaveChanges(); + + var enrollments = new Enrollment[] + { + new Enrollment + { + Student = students[0], + Course = courses[0], + Grade = Grade.A + }, + new Enrollment + { + Student = students[0], + Course = courses[2], + Grade = Grade.C + }, + new Enrollment + { + Student = students[0], + Course = courses[3], + Grade = Grade.B + }, + new Enrollment + { + Student = students[1], + Course = courses[1], + Grade = Grade.B + }, + new Enrollment + { + Student = students[1], + Course = courses[4], + Grade = Grade.F + }, + new Enrollment + { + Student = students[1], + Course = courses[4], + Grade = Grade.F + }, + new Enrollment { Student = students[2], Course = courses[0] }, + new Enrollment { Student = students[3], Course = courses[0] }, + new Enrollment + { + Student = students[3], + Course = courses[2], + Grade = Grade.F + }, + new Enrollment + { + Student = students[4], + Course = courses[3], + Grade = Grade.C + }, + new Enrollment { Student = students[5], Course = courses[1] }, + new Enrollment + { + Student = students[6], + Course = courses[4], + Grade = Grade.A + }, + }; + foreach (Enrollment e in enrollments) + { + context.Enrollments.Add(e); + } + context.SaveChanges(); + } +} diff --git a/amorphie.template.data/Migrations/20230602060216_initialize.Designer.cs b/amorphie.template.data/Migrations/20230602060216_initialize.Designer.cs new file mode 100644 index 0000000..cdab140 --- /dev/null +++ b/amorphie.template.data/Migrations/20230602060216_initialize.Designer.cs @@ -0,0 +1,123 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using amorphie.template.data; + +#nullable disable + +namespace amorphie.template.data.Migrations +{ + [DbContext(typeof(TemplateDbContext))] + [Migration("20230602060216_initialize")] + partial class initialize + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Credits") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CourseId") + .HasColumnType("uuid"); + + b.Property("Grade") + .HasColumnType("integer"); + + b.Property("StudentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("StudentId"); + + b.ToTable("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EnrollmentDate") + .HasColumnType("timestamp with time zone"); + + b.Property("FirstMidName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.HasOne("amorphie.template.core.Model.Course", "Course") + .WithMany("Enrollments") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("amorphie.template.core.Model.Student", "Student") + .WithMany("Enrollments") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Navigation("Enrollments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/amorphie.template.data/Migrations/20230602060216_initialize.cs b/amorphie.template.data/Migrations/20230602060216_initialize.cs new file mode 100644 index 0000000..2a9c050 --- /dev/null +++ b/amorphie.template.data/Migrations/20230602060216_initialize.cs @@ -0,0 +1,91 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace amorphie.template.data.Migrations +{ + /// + public partial class initialize : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Courses", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Title = table.Column(type: "text", nullable: false), + Credits = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Courses", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Students", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + LastName = table.Column(type: "text", nullable: false), + FirstMidName = table.Column(type: "text", nullable: false), + EnrollmentDate = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Students", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Enrollments", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Grade = table.Column(type: "integer", nullable: true), + CourseId = table.Column(type: "uuid", nullable: false), + StudentId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Enrollments", x => x.Id); + table.ForeignKey( + name: "FK_Enrollments_Courses_CourseId", + column: x => x.CourseId, + principalTable: "Courses", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Enrollments_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Enrollments_CourseId", + table: "Enrollments", + column: "CourseId"); + + migrationBuilder.CreateIndex( + name: "IX_Enrollments_StudentId", + table: "Enrollments", + column: "StudentId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Enrollments"); + + migrationBuilder.DropTable( + name: "Courses"); + + migrationBuilder.DropTable( + name: "Students"); + } + } +} diff --git a/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.Designer.cs b/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.Designer.cs new file mode 100644 index 0000000..b84df9d --- /dev/null +++ b/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.Designer.cs @@ -0,0 +1,177 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using amorphie.template.data; + +#nullable disable + +namespace amorphie.template.data.Migrations +{ + [DbContext(typeof(TemplateDbContext))] + [Migration("20230602081915_EntityBaseSupport")] + partial class EntityBaseSupport + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Credits") + .HasColumnType("integer"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CourseId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Grade") + .HasColumnType("integer"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("StudentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("StudentId"); + + b.ToTable("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("EnrollmentDate") + .HasColumnType("timestamp with time zone"); + + b.Property("FirstMidName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.HasOne("amorphie.template.core.Model.Course", "Course") + .WithMany("Enrollments") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("amorphie.template.core.Model.Student", "Student") + .WithMany("Enrollments") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Navigation("Enrollments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.cs b/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.cs new file mode 100644 index 0000000..6223dff --- /dev/null +++ b/amorphie.template.data/Migrations/20230602081915_EntityBaseSupport.cs @@ -0,0 +1,211 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace amorphie.template.data.Migrations +{ + /// + public partial class EntityBaseSupport : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Students", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Students", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "CreatedByBehalfOf", + table: "Students", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ModifiedAt", + table: "Students", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "ModifiedBy", + table: "Students", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "ModifiedByBehalfOf", + table: "Students", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Enrollments", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Enrollments", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "CreatedByBehalfOf", + table: "Enrollments", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ModifiedAt", + table: "Enrollments", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "ModifiedBy", + table: "Enrollments", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "ModifiedByBehalfOf", + table: "Enrollments", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Courses", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Courses", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "CreatedByBehalfOf", + table: "Courses", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ModifiedAt", + table: "Courses", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "ModifiedBy", + table: "Courses", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "ModifiedByBehalfOf", + table: "Courses", + type: "uuid", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Students"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Students"); + + migrationBuilder.DropColumn( + name: "CreatedByBehalfOf", + table: "Students"); + + migrationBuilder.DropColumn( + name: "ModifiedAt", + table: "Students"); + + migrationBuilder.DropColumn( + name: "ModifiedBy", + table: "Students"); + + migrationBuilder.DropColumn( + name: "ModifiedByBehalfOf", + table: "Students"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "CreatedByBehalfOf", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "ModifiedAt", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "ModifiedBy", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "ModifiedByBehalfOf", + table: "Enrollments"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Courses"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Courses"); + + migrationBuilder.DropColumn( + name: "CreatedByBehalfOf", + table: "Courses"); + + migrationBuilder.DropColumn( + name: "ModifiedAt", + table: "Courses"); + + migrationBuilder.DropColumn( + name: "ModifiedBy", + table: "Courses"); + + migrationBuilder.DropColumn( + name: "ModifiedByBehalfOf", + table: "Courses"); + } + } +} diff --git a/amorphie.template.data/Migrations/TemplateDbContextModelSnapshot.cs b/amorphie.template.data/Migrations/TemplateDbContextModelSnapshot.cs new file mode 100644 index 0000000..c495da0 --- /dev/null +++ b/amorphie.template.data/Migrations/TemplateDbContextModelSnapshot.cs @@ -0,0 +1,174 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using amorphie.template.data; + +#nullable disable + +namespace amorphie.template.data.Migrations +{ + [DbContext(typeof(TemplateDbContext))] + partial class TemplateDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Credits") + .HasColumnType("integer"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CourseId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("Grade") + .HasColumnType("integer"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("StudentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("StudentId"); + + b.ToTable("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByBehalfOf") + .HasColumnType("uuid"); + + b.Property("EnrollmentDate") + .HasColumnType("timestamp with time zone"); + + b.Property("FirstMidName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ModifiedBy") + .HasColumnType("uuid"); + + b.Property("ModifiedByBehalfOf") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Enrollment", b => + { + b.HasOne("amorphie.template.core.Model.Course", "Course") + .WithMany("Enrollments") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("amorphie.template.core.Model.Student", "Student") + .WithMany("Enrollments") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Course", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("amorphie.template.core.Model.Student", b => + { + b.Navigation("Enrollments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/amorphie.template.data/TemplateDbContext.cs b/amorphie.template.data/TemplateDbContext.cs new file mode 100644 index 0000000..7bacc4e --- /dev/null +++ b/amorphie.template.data/TemplateDbContext.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using amorphie.template.core.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace amorphie.template.data; + +class TemplateDbContextFactory : IDesignTimeDbContextFactory +{ + //lazy loading true + //lazy loading false, eğer alt bileşenleri getirmek istiyorsak include kullanmamız lazım,eager loading + private readonly IConfiguration _configuration; + + public TemplateDbContextFactory() { } + + public TemplateDbContextFactory(IConfiguration configuration) + { + _configuration = configuration; + } + + public TemplateDbContext CreateDbContext(string[] args) + { + var builder = new DbContextOptionsBuilder(); + // var test = _configuration["STATE_STORE"]; + // System.Console.WriteLine("Test: " + test); + + + var connStr = "Host=localhost:5432;Database=TemplateDb;Username=postgres;Password=postgres"; + builder.UseNpgsql(connStr); + builder.EnableSensitiveDataLogging(); + return new TemplateDbContext(builder.Options); + } +} + +public class TemplateDbContext : DbContext +{ + public TemplateDbContext(DbContextOptions options) + : base(options) { } + + public DbSet Students { get; set; } + public DbSet Enrollments { get; set; } + public DbSet Courses { get; set; } +} diff --git a/amorphie.template.data/amorphie.template.data.csproj b/amorphie.template.data/amorphie.template.data.csproj new file mode 100644 index 0000000..05d11c3 --- /dev/null +++ b/amorphie.template.data/amorphie.template.data.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/amorphie.template.hub/Program.cs b/amorphie.template.hub/Program.cs new file mode 100644 index 0000000..46d5a55 --- /dev/null +++ b/amorphie.template.hub/Program.cs @@ -0,0 +1,48 @@ +using Prometheus; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast") +.WithOpenApi(); + +app.MapMetrics(); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/amorphie.template.hub/Properties/launchSettings.json b/amorphie.template.hub/Properties/launchSettings.json new file mode 100644 index 0000000..78dbe55 --- /dev/null +++ b/amorphie.template.hub/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50824", + "sslPort": 44315 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7221;http://localhost:5041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/amorphie.template.hub/amorphie.template.hub.csproj b/amorphie.template.hub/amorphie.template.hub.csproj new file mode 100644 index 0000000..c108c45 --- /dev/null +++ b/amorphie.template.hub/amorphie.template.hub.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/amorphie.template.hub/appsettings.Development.json b/amorphie.template.hub/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/amorphie.template.hub/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/amorphie.template.hub/appsettings.json b/amorphie.template.hub/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/amorphie.template.hub/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/amorphie.template.test/Usings.cs b/amorphie.template.test/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/amorphie.template.test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/amorphie.template.test/amorphie.template.test.csproj b/amorphie.template.test/amorphie.template.test.csproj new file mode 100644 index 0000000..2e21250 --- /dev/null +++ b/amorphie.template.test/amorphie.template.test.csproj @@ -0,0 +1,34 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/amorphie.template.test/architecture/DependencyCheck.cs b/amorphie.template.test/architecture/DependencyCheck.cs new file mode 100644 index 0000000..c3daead --- /dev/null +++ b/amorphie.template.test/architecture/DependencyCheck.cs @@ -0,0 +1,19 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; + +namespace amorphie.template.test.architecture; + +public class DependencyCheck +{ + [Fact] + public void CoreDependencyCheck() + { + var result = Types.InCurrentDomain() + .That() + .ResideInNamespace("amorphie.template.core") + .ShouldNot() + .HaveDependencyOn("amorphie.template.data") + .GetResult() + .IsSuccessful; + } +} diff --git a/amorphie.template.test/architecture/ModuleCheck.cs b/amorphie.template.test/architecture/ModuleCheck.cs new file mode 100644 index 0000000..8134564 --- /dev/null +++ b/amorphie.template.test/architecture/ModuleCheck.cs @@ -0,0 +1,50 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; +using amorphie.template.Module; + +namespace amorphie.template.test.architecture; + +public class ModuleCheck +{ + public dynamic GetModules() + { + var types = Types.InAssembly(typeof(StudentModule).Assembly); + return types.That().ResideInNamespace("amorphie.template.Module"); + } + + [Fact] + public void CheckModuleName() + { + var modules = GetModules(); + + var result = modules.Should().HaveNameEndingWith("Module") + .GetResult() + .IsSuccessful; + + Assert.True(result); + } + + [Fact] + public void IsModuleSealed() + { + var modules = GetModules(); + + var result = modules.Should().BeSealed() + .GetResult() + .IsSuccessful; + + Assert.True(result); + } + + [Fact] + public void IsInheritedFromCore() + { + var modules = GetModules(); + + var result = modules.Should().Inherit(typeof(BaseBBTRoute<,,>)).Or().Inherit(typeof(BaseRoute)) + .GetResult() + .IsSuccessful; + + Assert.True(result); + } +} diff --git a/amorphie.template.test/architecture/TypeCheck.cs b/amorphie.template.test/architecture/TypeCheck.cs new file mode 100644 index 0000000..d1f5213 --- /dev/null +++ b/amorphie.template.test/architecture/TypeCheck.cs @@ -0,0 +1,21 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; +using amorphie.template.Module; + +namespace amorphie.template.test.architecture; + +public class TypeCheck +{ + [Fact] + public void CheckIfAnyStatic() + { + var types = Types.InAssembly(typeof(StudentModule).Assembly); + + var result = types.Should().BeStatic() + .GetResult() + .IsSuccessful; + + Assert.False(result); + } + +} diff --git a/amorphie.template.worker/Program.cs b/amorphie.template.worker/Program.cs new file mode 100644 index 0000000..79e1376 --- /dev/null +++ b/amorphie.template.worker/Program.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc; +using Prometheus; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + + + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.MapPost("/change-state", async void ( + [FromBody] dynamic body, + HttpRequest request, + HttpContext httpContext) + => +{ + + Console.WriteLine(request.Headers["X-Zeebe-Element-Instance-Key"].ToString()); +}) +.WithOpenApi(); + + +app.UseCloudEvents(); +app.UseRouting(); +app.MapSubscribeHandler(); +app.UseSwagger(); +app.UseSwaggerUI(); + + + +app.MapMetrics(); + +app.Run(); + diff --git a/amorphie.template.worker/Properties/launchSettings.json b/amorphie.template.worker/Properties/launchSettings.json new file mode 100644 index 0000000..cef6782 --- /dev/null +++ b/amorphie.template.worker/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55768", + "sslPort": 44392 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5140", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7130;http://localhost:5140", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/amorphie.template.worker/amorphie.template.worker.csproj b/amorphie.template.worker/amorphie.template.worker.csproj new file mode 100644 index 0000000..af2a281 --- /dev/null +++ b/amorphie.template.worker/amorphie.template.worker.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/amorphie.template.worker/appsettings.Development.json b/amorphie.template.worker/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/amorphie.template.worker/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/amorphie.template.worker/appsettings.json b/amorphie.template.worker/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/amorphie.template.worker/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/amorphie.template/Mapper/ResourceMapper.cs b/amorphie.template/Mapper/ResourceMapper.cs new file mode 100644 index 0000000..e670d28 --- /dev/null +++ b/amorphie.template/Mapper/ResourceMapper.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.template.core.Model; +using AutoMapper; + +namespace amorphie.template.Mapper +{ + public sealed class ResourceMapper : Profile + { + public ResourceMapper() + { + CreateMap().ReverseMap(); + } + } +} diff --git a/amorphie.template/Module/StudentModule.cs b/amorphie.template/Module/StudentModule.cs new file mode 100644 index 0000000..937f9a0 --- /dev/null +++ b/amorphie.template/Module/StudentModule.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using amorphie.template.core.Model; +using amorphie.template.Validator; +using amorphie.core.Module.minimal_api; +using amorphie.template.data; +using Microsoft.AspNetCore.Mvc; +using amorphie.template.core.Search; +using Microsoft.EntityFrameworkCore; +using AutoMapper; +using amorphie.core.Swagger; +using Microsoft.OpenApi.Models; +using amorphie.core.Identity; + +namespace amorphie.template.Module; + + +public sealed class StudentModule : BaseBBTRoute +{ + public StudentModule(WebApplication app) + : base(app) { } + + public override string[]? PropertyCheckList => new string[] { "FirstMidName", "LastName" }; + + public override string? UrlFragment => "student"; + + public override void AddRoutes(RouteGroupBuilder routeGroupBuilder) + { + base.AddRoutes(routeGroupBuilder); + + routeGroupBuilder.MapGet("/search", SearchMethod); + routeGroupBuilder.MapGet("/custom-method", CustomMethod); + } + protected override ValueTask UpsertMethod([FromServices] IMapper mapper, [FromServices] FluentValidation.IValidator validator, [FromServices] TemplateDbContext context, [FromServices] IBBTIdentity bbtIdentity, [FromBody] StudentDTO data, HttpContext httpContext, CancellationToken token) + { + return base.UpsertMethod(mapper, validator, context, bbtIdentity, data, httpContext, token); + } + [AddSwaggerParameter("Test Required", ParameterLocation.Header, true)] + protected async ValueTask CustomMethod() + { + return Results.Ok(); + } + + protected async ValueTask SearchMethod( + [FromServices] TemplateDbContext context, + [FromServices] IMapper mapper, + [AsParameters] StudentSearch userSearch, + HttpContext httpContext, + CancellationToken token + ) + { + IList resultList = await context + .Set() + .AsNoTracking() + .Where( + x => + x.FirstMidName.Contains(userSearch.Keyword!) + || x.LastName.Contains(userSearch.Keyword!) + ) + .Skip(userSearch.Page) + .Take(userSearch.PageSize) + .ToListAsync(token); + + return (resultList != null && resultList.Count > 0) + ? Results.Ok(mapper.Map>(resultList)) + : Results.NoContent(); + } +} diff --git a/amorphie.template/Program.cs b/amorphie.template/Program.cs new file mode 100644 index 0000000..0b14b12 --- /dev/null +++ b/amorphie.template/Program.cs @@ -0,0 +1,79 @@ +using System.Text.Json.Serialization; +using amorphie.core.Extension; +using amorphie.core.HealthCheck; +using amorphie.core.Identity; +using amorphie.core.Swagger; +using amorphie.template.data; +using amorphie.template.HealthCheck; +using amorphie.template.Validator; +using FluentValidation; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Http.Json; +using Microsoft.EntityFrameworkCore; +using Npgsql.Replication; +using Prometheus; + + + +var builder = WebApplication.CreateBuilder(args); +await builder.Configuration.AddVaultSecrets("amorphie-secretstore", new string[] { "amorphie-template" }); +var postgreSql = builder.Configuration["templatedb"]; + +// If you use AutoInclude in context you should add ReferenceHandler.IgnoreCycles to avoid circular load +builder.Services.Configure(options => +{ + options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.SerializerOptions.WriteIndented = true; +}); + +builder.Services.AddDaprClient(); +builder.Services.AddHealthChecks().AddBBTHealthCheck(); + +builder.Services.AddScoped(); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + options.OperationFilter(); +}); + +builder.Services.AddValidatorsFromAssemblyContaining(includeInternalTypes: true); +builder.Services.AddAutoMapper(typeof(Program).Assembly); + + + +builder.Services.AddDbContext + (options => options.UseNpgsql(postgreSql, b => b.MigrationsAssembly("amorphie.template.data"))); + + +var app = builder.Build(); + + +using var scope = app.Services.CreateScope(); +var db = scope.ServiceProvider.GetRequiredService(); + +db.Database.Migrate(); +DbInitializer.Initialize(db); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.AddRoutes(); + +app.MapHealthChecks("/healthz", new HealthCheckOptions +{ + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); +app.MapMetrics(); + +app.Run(); + diff --git a/amorphie.template/Properties/launchSettings.json b/amorphie.template/Properties/launchSettings.json new file mode 100644 index 0000000..7146fbf --- /dev/null +++ b/amorphie.template/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1699", + "sslPort": 44315 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5111", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7064;http://localhost:5111", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/amorphie.template/Validator/StudentValidator.cs b/amorphie.template/Validator/StudentValidator.cs new file mode 100644 index 0000000..0e55335 --- /dev/null +++ b/amorphie.template/Validator/StudentValidator.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentValidation; +using amorphie.template.core.Model; + +namespace amorphie.template.Validator; + public sealed class StudentValidator : AbstractValidator + { + public StudentValidator() + { + RuleFor(x => x.FirstMidName).NotNull(); + RuleFor(x => x.LastName).MinimumLength(10); + } + } + +public static class HealthCheckModule +{ + +} + diff --git a/amorphie.template/amorphie.template.csproj b/amorphie.template/amorphie.template.csproj new file mode 100644 index 0000000..277b4e4 --- /dev/null +++ b/amorphie.template/amorphie.template.csproj @@ -0,0 +1,36 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/amorphie.template/appsettings.Development.json b/amorphie.template/appsettings.Development.json new file mode 100644 index 0000000..1b2d3ba --- /dev/null +++ b/amorphie.template/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} \ No newline at end of file diff --git a/amorphie.template/appsettings.json b/amorphie.template/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/amorphie.template/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/components/amorphie-config.yaml b/dapr/components/amorphie-config.yaml new file mode 100644 index 0000000..527d388 --- /dev/null +++ b/dapr/components/amorphie-config.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: amorphie-config +spec: + type: configuration.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/dapr/components/amorphie-queue.yaml b/dapr/components/amorphie-queue.yaml new file mode 100644 index 0000000..891d2d3 --- /dev/null +++ b/dapr/components/amorphie-queue.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: amorphie-queue +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/dapr/components/amorphie-secretstore.yaml b/dapr/components/amorphie-secretstore.yaml new file mode 100644 index 0000000..003b991 --- /dev/null +++ b/dapr/components/amorphie-secretstore.yaml @@ -0,0 +1,18 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: amorphie-secretstore +spec: + type: secretstores.hashicorp.vault + version: v1 + metadata: + - name: vaultAddr + value: http://localhost:8200 + - name: vaultToken + value: "admin" + - name: skipVerify + value: true + - name: vaultKVUsePrefix + value: false + - name: enginePath + value: "secret" \ No newline at end of file diff --git a/dapr/components/amorphie-state.yaml b/dapr/components/amorphie-state.yaml new file mode 100644 index 0000000..cfed196 --- /dev/null +++ b/dapr/components/amorphie-state.yaml @@ -0,0 +1,14 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: amorphie-state +spec: + type: state.redis + version: v1 + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + - name: actorStateStore + value: "true" \ No newline at end of file diff --git a/dapr/components/amorphie-zebee-local.yaml b/dapr/components/amorphie-zebee-local.yaml new file mode 100644 index 0000000..2aa26b3 --- /dev/null +++ b/dapr/components/amorphie-zebee-local.yaml @@ -0,0 +1,14 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zeebe-local +spec: + type: bindings.zeebe.command + version: v1 + metadata: + - name: gatewayAddr + value: localhost:26500 + - name: gatewayKeepAlive + value: 45s + - name: usePlainTextConnection + value: true \ No newline at end of file diff --git a/dapr/components/amorphie-zebee-workflow-completed.yaml b/dapr/components/amorphie-zebee-workflow-completed.yaml new file mode 100644 index 0000000..5fdd39c --- /dev/null +++ b/dapr/components/amorphie-zebee-workflow-completed.yaml @@ -0,0 +1,16 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: change-state +spec: + type: bindings.zeebe.jobworker + version: v1 + metadata: + - name: jobType + value: change-state + - name: gatewayAddr + value: localhost:26500 + - name: gatewayKeepAlive + value: 45s + - name: usePlainTextConnection + value: true \ No newline at end of file diff --git a/dapr/config.yaml b/dapr/config.yaml new file mode 100644 index 0000000..22f97ac --- /dev/null +++ b/dapr/config.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig + namespace: default +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://localhost:9411/api/v2/spans" + diff --git a/docker/config/prometheus/hostname b/docker/config/prometheus/hostname new file mode 100644 index 0000000..e69de29 diff --git a/docker/config/prometheus/hosts b/docker/config/prometheus/hosts new file mode 100644 index 0000000..e69de29 diff --git a/docker/config/prometheus/prometheus.yml b/docker/config/prometheus/prometheus.yml new file mode 100644 index 0000000..d0e5fb0 --- /dev/null +++ b/docker/config/prometheus/prometheus.yml @@ -0,0 +1,14 @@ +global: + scrape_interval: 5s + +scrape_configs: + - job_name: "dapr" + scrape_interval: 5s + + static_configs: + - targets: + [ + "host.docker.internal:4200", + "host.docker.internal:4201", + "host.docker.internal:4202", + ] \ No newline at end of file diff --git a/docker/config/prometheus/resolv.conf b/docker/config/prometheus/resolv.conf new file mode 100644 index 0000000..e69de29 diff --git a/docker/config/zeebe-config.yaml b/docker/config/zeebe-config.yaml new file mode 100644 index 0000000..3b150da --- /dev/null +++ b/docker/config/zeebe-config.yaml @@ -0,0 +1,135 @@ +--- # ---------------------------------------------------- + +# Zeebe Standalone Broker configuration file (with embedded gateway) + +# This file is based on broker.standalone.yaml.template but stripped down to contain only a limited +# set of configuration options. These are a good starting point to get to know Zeebe. +# For advanced configuration options, have a look at the templates in this folder. + +# !!! Note that this configuration is not suitable for running a standalone gateway. !!! +# If you want to run a standalone gateway node, please have a look at gateway.yaml.template + +# ---------------------------------------------------- +# Byte sizes +# For buffers and others must be specified as strings and follow the following +# format: "10U" where U (unit) must be replaced with KB = Kilobytes, MB = Megabytes or GB = Gigabytes. +# If unit is omitted then the default unit is simply bytes. +# Example: +# sendBufferSize = "16MB" (creates a buffer of 16 Megabytes) +# +# Time units +# Timeouts, intervals, and the likes, must be specified either in the standard ISO-8601 format used +# by java.time.Duration, or as strings with the following format: "VU", where: +# - V is a numerical value (e.g. 1, 5, 10, etc.) +# - U is the unit, one of: ms = Millis, s = Seconds, m = Minutes, or h = Hours +# +# Paths: +# Relative paths are resolved relative to the installation directory of the broker. +zeebe: + broker: + gateway: + # Enable the embedded gateway to start on broker startup. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_GATEWAY_ENABLE. + enable: true + + network: + # Sets the port the embedded gateway binds to. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_GATEWAY_NETWORK_PORT. + port: 26500 + + security: + # Enables TLS authentication between clients and the gateway + # This setting can also be overridden using the environment variable ZEEBE_BROKER_GATEWAY_SECURITY_ENABLED. + enabled: false + + network: + # Controls the default host the broker should bind to. Can be overwritten on a + # per binding basis for client, management and replication + # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_HOST. + host: 0.0.0.0 + + data: + # Specify a list of directories in which data is stored. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_DATA_DIRECTORIES. + directories: [data] + # The size of data log segment files. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_DATA_LOGSEGMENTSIZE. + logSegmentSize: 512MB + # How often we take snapshots of streams (time unit) + # This setting can also be overridden using the environment variable ZEEBE_BROKER_DATA_SNAPSHOTPERIOD. + snapshotPeriod: 15m + + cluster: + # Specifies the Zeebe cluster size. + # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CLUSTERSIZE. + clusterSize: 1 + # Controls the replication factor, which defines the count of replicas per partition. + # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR. + replicationFactor: 1 + # Controls the number of partitions, which should exist in the cluster. + # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT. + partitionsCount: 1 + + threads: + # Controls the number of non-blocking CPU threads to be used. + # WARNING: You should never specify a value that is larger than the number of physical cores + # available. Good practice is to leave 1-2 cores for ioThreads and the operating + # system (it has to run somewhere). For example, when running Zeebe on a machine + # which has 4 cores, a good value would be 2. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_THREADS_CPUTHREADCOUNT + cpuThreadCount: 2 + # Controls the number of io threads to be used. + # This setting can also be overridden using the environment variable ZEEBE_BROKER_THREADS_IOTHREADCOUNT + ioThreadCount: 2 + # Elasticsearch Exporter ---------- + # An example configuration for the elasticsearch exporter: + # + # These setting can also be overridden using the environment variables "ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_..." + # + exporters: + redis: + className: io.zeebe.redis.exporter.RedisExporter + jarPath: exporters/zeebe-redis-exporter-jar-with-dependencies.jar + args: + remoteAddress: redis://bbt-template-redis:6379 + # Redis Stream prefix + name: "zeebe" + # record serialization format: [protobuf|json] + format: "json" + + elasticsearch: + className: io.camunda.zeebe.exporter.ElasticsearchExporter + + args: + url: http://bbt-template-elastic:9200 + + bulk: + delay: 5 + size: 1000 + + # authentication: + # username: elastic + # password: changeme + + index: + prefix: zeebe-record + createTemplate: true + + command: false + event: true + rejection: false + + deployment: true + error: true + incident: true + job: true + jobBatch: false + message: false + messageSubscription: false + variable: true + variableDocument: true + workflowInstance: true + workflowInstanceCreation: false + workflowInstanceSubscription: false + + ignoreVariablesAbove: 32677 \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..123f4a5 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,198 @@ +version: "3.9" +services: + + redis: + container_name: bbt-template-redis + image: redis:latest + restart: always + ports: + - '6379:6379' + command: redis-server --save 20 1 --loglevel warning + volumes: + - redis:/data + networks: + - bbt-development + + redisinsight: + container_name: bbt-template-red-insight + image: redislabs/redisinsight:latest + ports: + - '5501:8001' + volumes: + - redisinsight:/db + restart: unless-stopped + networks: + - bbt-development + + zeebe: + container_name: bbt-template-zeebe + image: camunda/zeebe:latest + environment: + - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME=io.camunda.zeebe.exporter.ElasticsearchExporter + - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_URL=http://bbt-template-elastic:9200 + - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_BULK_SIZE=1 + - ZEEBE_LOG_LEVEL=debug + - ZEEBE_REDIS_REMOTE_ADDRESS=redis://bbt-template-redis:6379 + - ZEEBE_REDIS_TIME_TO_LIVE_IN_SECONDS=900 + depends_on: + - elastic + - redis + ports: + - "26500:26500" + - "9600:9600" + volumes: + - ./config/zeebe-config.yaml:/usr/local/zeebe/config/application.yaml + - ./zeebe-exporters/zeebe-redis-exporter-0.9.2-jar-with-dependencie.jar:/usr/local/zeebe/exporters/zeebe-redis-exporter-jar-with-dependencies.jar + networks: + - bbt-development + + operate: + container_name: bbt-template-zeebe-operate + image: camunda/operate:latest + ports: + - "8081:8080" + environment: + - CAMUNDA_OPERATE_ZEEBE_GATEWAYADDRESS=bbt-template-zeebe:26500 + - CAMUNDA_OPERATE_ELASTICSEARCH_URL=http://bbt-template-elastic:9200 + - CAMUNDA_OPERATE_ZEEBEELASTICSEARCH_URL=http://bbt-template-elastic:9200 + networks: + - bbt-development + depends_on: + - zeebe + - elastic + + postgres: + container_name: bbt-template-postgres + image: postgres:latest + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + PGDATA: /data/postgres + volumes: + - postgres:/data/postgres + ports: + - "5432:5432" + restart: unless-stopped + networks: + - bbt-development + + pgadmin: + container_name: bbt-template-pgadmin + image: dpage/pgadmin4:latest + environment: + PGADMIN_DEFAULT_EMAIL: "info@info.com" + PGADMIN_DEFAULT_PASSWORD: admin + PGADMIN_CONFIG_SERVER_MODE: 'False' + volumes: + - pgadmin:/var/lib/pgadmin + ports: + - "5502:80" + restart: unless-stopped + networks: + - bbt-development + + elastic: + container_name: bbt-template-elastic + image: elasticsearch:8.8.1 + environment: + - xpack.security.enabled=false + - "discovery.type=single-node" + networks: + - bbt-development + ports: + - 9200:9200 + + kibana: + container_name: bbt-template-kibana + image: kibana:8.8.1 + environment: + - ELASTICSEARCH_HOSTS=http://bbt-template-elastic:9200 + networks: + - bbt-development + depends_on: + - elastic + ports: + - 5601:5601 + + vault: + container_name: bbt-template-vault + image: vault:latest + restart: on-failure:10 + ports: + - "8200:8200" + environment: + VAULT_ADDR: 'https://0.0.0.0:8200' + VAULT_API_ADDR: 'https://0.0.0.0:8200' + VAULT_DEV_ROOT_TOKEN_ID: 'admin' + VAULT_TOKEN: 'admin' + volumes: + - ./file:/vault/file + cap_add: + - IPC_LOCK + healthcheck: + retries: 5 + command: server -dev -dev-root-token-id="admin" + networks: + - bbt-development + + vault-prepopulate: + image: alpine/curl:latest + depends_on: + - vault + volumes: + - ./vault.sh:/usr/local/bin/prepopulate_vault.sh + command: ["sh", "-c", "/usr/local/bin/prepopulate_vault.sh"] + networks: + - bbt-development + + zipkin: + container_name: bbt-template-zipkin + image: openzipkin/zipkin:latest + ports: + - "9411:9411" + networks: + - bbt-development + + prometheus: + container_name: bbt-template-prometheus + image: prom/prometheus:latest + command: + - "--config.file=/etc/prometheus.yml" + volumes: + - ./config/prometheus:/etc + ports: + - "9090:9090" + networks: + - bbt-development + + grafana: + container_name: bbt-template-grafana + image: grafana/grafana:latest + ports: + - "3000:3000" + networks: + - bbt-development + depends_on: + - prometheus + + placement: + image: daprio/dapr:latest + command: ["./placement", "-port", "50006", "log-level", "debug", "--log-as-json"] + ports: + - "50006:50006" + networks: + - bbt-development + +networks: + bbt-development: + external: true + +volumes: + redis: + redisinsight: + postgres: + pgadmin: + dapr: + grafana-data: + prometheus: + diff --git a/docker/redis.sh b/docker/redis.sh new file mode 100644 index 0000000..5d7df6c --- /dev/null +++ b/docker/redis.sh @@ -0,0 +1,5 @@ +redis-server --daemonize yes && sleep 3 +redis-cli MSET config-amorphie-contract-db 'User ID=postgres;Password=postgres;Host=Localhost;Port=5432;Database=contract;' +redis-cli save +redis-cli shutdown +redis-server \ No newline at end of file diff --git a/docker/vault.sh b/docker/vault.sh new file mode 100644 index 0000000..e63e466 --- /dev/null +++ b/docker/vault.sh @@ -0,0 +1,2 @@ +sleep 5 && +curl -X POST 'http://bbt-template-vault:8200/v1/secret/data/amorphie-template' -H "Content-Type: application/json" -H "X-Vault-Token: admin" -d '{ "data": {"templatedb":"Host=localhost:5432;Database=TemplateDb;Username=postgres;Password=postgres;Include Error Detail=true;"} }' diff --git a/docker/zeebe-exporters/zeebe-redis-exporter-0.9.2-jar-with-dependencie.jar b/docker/zeebe-exporters/zeebe-redis-exporter-0.9.2-jar-with-dependencie.jar new file mode 100644 index 0000000..328f8d3 Binary files /dev/null and b/docker/zeebe-exporters/zeebe-redis-exporter-0.9.2-jar-with-dependencie.jar differ diff --git a/docs/images/user-register.png b/docs/images/user-register.png new file mode 100644 index 0000000..8c9e04b Binary files /dev/null and b/docs/images/user-register.png differ diff --git a/technical-spec.md b/technical-spec.md new file mode 100644 index 0000000..685ea25 --- /dev/null +++ b/technical-spec.md @@ -0,0 +1,51 @@ +1. Introduction: + - [Provide an overview of the project and its goals.] + +2. Architectural Overview: + - [Describe the microservices architecture and its benefits.] + +3. System Requirements: + - Functional requirements: + - [Specify the required functionalities of the project.] + - Non-functional requirements: + - [Specify the non-functional requirements like scalability and security.] + +4. User Interface: + - [Explain the need for a user interface, if any.] + +5. Data Model: + - [Describe the entities and their relationships in the data model.] + +6. System Components (Microservices): + - [List the microservices involved and their responsibilities.] + +7. APIs and Services: + - [Outline the API endpoints for each microservice.] + +8. Algorithms and Business Logic: + - [Explain the algorithms and business Logic.] + +9. Performance and Scalability: + - [Discuss mechanisms for performance optimization and scalability.] + +10. Security and Privacy: + - [Specify the security measures to protect sensitive information.] + +11. Testing and Quality Assurance: + - [Describe the testing strategies and frameworks to ensure quality.] + +12. Deployment and Infrastructure: + - [Explain the deployment process on OpenShift and utilization of containerization technologies.] + +13. Error Handling and Logging: + - [Discuss the error handling mechanisms and logging frameworks.] + +14. Real-time Communication: + - [Describe the integration of SignalR for real-time communication.] + +15. Maintenance and Support: + - [Specify the tools and processes for issue tracking and version control.] + +16. References: + - [List external references used during development.] + diff --git a/template.sln b/template.sln new file mode 100644 index 0000000..dee4d53 --- /dev/null +++ b/template.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.001.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template", "amorphie.template\amorphie.template.csproj", "{6F70799F-9DC5-4A27-A018-74F94DAC4495}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template.core", "amorphie.template.core\amorphie.template.core.csproj", "{4F22DAAF-2DF7-49D5-9794-CFD7D0830C71}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template.data", "amorphie.template.data\amorphie.template.data.csproj", "{F5537A5B-4B4B-4533-BC2F-8C0D87ED70B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template.hub", "amorphie.template.hub\amorphie.template.hub.csproj", "{27158190-3CC4-4AAC-B57F-37BA6D7671E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template.test", "amorphie.template.test\amorphie.template.test.csproj", "{336C2ABE-D5FD-40D8-B3F1-F572E4DCAD6F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "amorphie.template.worker", "amorphie.template.worker\amorphie.template.worker.csproj", "{272D933D-2463-4F9B-9F07-228557A08368}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F70799F-9DC5-4A27-A018-74F94DAC4495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F70799F-9DC5-4A27-A018-74F94DAC4495}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F70799F-9DC5-4A27-A018-74F94DAC4495}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F70799F-9DC5-4A27-A018-74F94DAC4495}.Release|Any CPU.Build.0 = Release|Any CPU + {4F22DAAF-2DF7-49D5-9794-CFD7D0830C71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F22DAAF-2DF7-49D5-9794-CFD7D0830C71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F22DAAF-2DF7-49D5-9794-CFD7D0830C71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F22DAAF-2DF7-49D5-9794-CFD7D0830C71}.Release|Any CPU.Build.0 = Release|Any CPU + {F5537A5B-4B4B-4533-BC2F-8C0D87ED70B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5537A5B-4B4B-4533-BC2F-8C0D87ED70B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5537A5B-4B4B-4533-BC2F-8C0D87ED70B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5537A5B-4B4B-4533-BC2F-8C0D87ED70B8}.Release|Any CPU.Build.0 = Release|Any CPU + {27158190-3CC4-4AAC-B57F-37BA6D7671E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27158190-3CC4-4AAC-B57F-37BA6D7671E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27158190-3CC4-4AAC-B57F-37BA6D7671E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27158190-3CC4-4AAC-B57F-37BA6D7671E5}.Release|Any CPU.Build.0 = Release|Any CPU + {336C2ABE-D5FD-40D8-B3F1-F572E4DCAD6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {336C2ABE-D5FD-40D8-B3F1-F572E4DCAD6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {336C2ABE-D5FD-40D8-B3F1-F572E4DCAD6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {336C2ABE-D5FD-40D8-B3F1-F572E4DCAD6F}.Release|Any CPU.Build.0 = Release|Any CPU + {272D933D-2463-4F9B-9F07-228557A08368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {272D933D-2463-4F9B-9F07-228557A08368}.Debug|Any CPU.Build.0 = Debug|Any CPU + {272D933D-2463-4F9B-9F07-228557A08368}.Release|Any CPU.ActiveCfg = Release|Any CPU + {272D933D-2463-4F9B-9F07-228557A08368}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {444177CF-6389-4C59-90B9-A269914B3DAC} + EndGlobalSection +EndGlobal diff --git a/tests/amorphie.template.test/Usings.cs b/tests/amorphie.template.test/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tests/amorphie.template.test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/tests/amorphie.template.test/amorphie.template.test.csproj b/tests/amorphie.template.test/amorphie.template.test.csproj new file mode 100644 index 0000000..2e21250 --- /dev/null +++ b/tests/amorphie.template.test/amorphie.template.test.csproj @@ -0,0 +1,34 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/tests/amorphie.template.test/architecture/DependencyCheck.cs b/tests/amorphie.template.test/architecture/DependencyCheck.cs new file mode 100644 index 0000000..c3daead --- /dev/null +++ b/tests/amorphie.template.test/architecture/DependencyCheck.cs @@ -0,0 +1,19 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; + +namespace amorphie.template.test.architecture; + +public class DependencyCheck +{ + [Fact] + public void CoreDependencyCheck() + { + var result = Types.InCurrentDomain() + .That() + .ResideInNamespace("amorphie.template.core") + .ShouldNot() + .HaveDependencyOn("amorphie.template.data") + .GetResult() + .IsSuccessful; + } +} diff --git a/tests/amorphie.template.test/architecture/ModuleCheck.cs b/tests/amorphie.template.test/architecture/ModuleCheck.cs new file mode 100644 index 0000000..8134564 --- /dev/null +++ b/tests/amorphie.template.test/architecture/ModuleCheck.cs @@ -0,0 +1,50 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; +using amorphie.template.Module; + +namespace amorphie.template.test.architecture; + +public class ModuleCheck +{ + public dynamic GetModules() + { + var types = Types.InAssembly(typeof(StudentModule).Assembly); + return types.That().ResideInNamespace("amorphie.template.Module"); + } + + [Fact] + public void CheckModuleName() + { + var modules = GetModules(); + + var result = modules.Should().HaveNameEndingWith("Module") + .GetResult() + .IsSuccessful; + + Assert.True(result); + } + + [Fact] + public void IsModuleSealed() + { + var modules = GetModules(); + + var result = modules.Should().BeSealed() + .GetResult() + .IsSuccessful; + + Assert.True(result); + } + + [Fact] + public void IsInheritedFromCore() + { + var modules = GetModules(); + + var result = modules.Should().Inherit(typeof(BaseBBTRoute<,,>)).Or().Inherit(typeof(BaseRoute)) + .GetResult() + .IsSuccessful; + + Assert.True(result); + } +} diff --git a/tests/amorphie.template.test/architecture/TypeCheck.cs b/tests/amorphie.template.test/architecture/TypeCheck.cs new file mode 100644 index 0000000..d1f5213 --- /dev/null +++ b/tests/amorphie.template.test/architecture/TypeCheck.cs @@ -0,0 +1,21 @@ +using amorphie.core.Module.minimal_api; +using NetArchTest.Rules; +using amorphie.template.Module; + +namespace amorphie.template.test.architecture; + +public class TypeCheck +{ + [Fact] + public void CheckIfAnyStatic() + { + var types = Types.InAssembly(typeof(StudentModule).Assembly); + + var result = types.Should().BeStatic() + .GetResult() + .IsSuccessful; + + Assert.False(result); + } + +} diff --git a/tests/collections/localhost.postman_environment.json b/tests/collections/localhost.postman_environment.json new file mode 100644 index 0000000..0bdab1f --- /dev/null +++ b/tests/collections/localhost.postman_environment.json @@ -0,0 +1,21 @@ +{ + "id": "f97ac088-dcf3-4288-90cb-8c910653ba14", + "name": "localhost", + "values": [ + { + "key": "ServerUrl", + "value": "http://host.docker.internal:4200", + "type": "default", + "enabled": true + }, + { + "key": "CreatedId", + "value": "", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2023-09-07T11:56:54.854Z", + "_postman_exported_using": "Postman/10.16.9" +} diff --git a/tests/collections/test_postman_collection.json b/tests/collections/test_postman_collection.json new file mode 100644 index 0000000..a949f2a --- /dev/null +++ b/tests/collections/test_postman_collection.json @@ -0,0 +1,247 @@ +{ + "info": { + "_postman_id": "c087242f-c5f8-470e-978a-322292609521", + "name": "Template-Integration-Test", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "17057612", + "_collection_link": "https://red-shadow-543286.postman.co/workspace/Integration-Test~e64ac9cf-2581-4551-912f-5ed44db9355d/collection/17057612-c087242f-c5f8-470e-978a-322292609521?action=share&creator=17057612&source=collection_link" + }, + "item": [ + { + "name": "Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"Response status code is 201\", function () {", + " pm.response.to.have.status(201);", + "});", + "", + "", + "pm.test(\"Response has the required fields\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.lastName).to.exist.and.to.be.a('string');", + " pm.expect(responseData.firstMidName).to.exist.and.to.be.a('string');", + " pm.expect(responseData.enrollmentDate).to.exist.and.to.be.a('string');", + " pm.expect(responseData.id).to.exist.and.to.be.a('string');", + "});", + "", + "", + "pm.test(\"lastName is a non-empty string\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData.lastName).to.be.a('string').and.to.have.lengthOf.at.least(1, \"Value should not be empty\");", + "});", + "", + "", + "pm.test(\"firstMidName is a non-empty string\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.firstMidName).to.be.a('string').and.to.have.lengthOf.at.least(1, \"Value should not be empty\");", + "});", + "", + "", + "pm.test(\"enrollmentDate is in a valid date format\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.enrollmentDate).to.match(/^\\d{4}-\\d{2}-\\d{2}$/);", + "});", + "", + "var jsonData = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"CreatedId\", jsonData.id);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"lastName\": \"EvansEvans\",\n \"firstMidName\": \"Joseph\",\n \"enrollmentDate\": \"2023-09-07T10:55:29.825Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{ServerUrl}}/student" + }, + "response": [] + }, + { + "name": "Get", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"Response status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "pm.test(\"Response has required fields - lastName, firstMidName, enrollmentDate, and id\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.lastName).to.exist.and.to.be.a('string');", + " pm.expect(responseData.firstMidName).to.exist.and.to.be.a('string');", + " pm.expect(responseData.enrollmentDate).to.exist.and.to.be.a('string');", + " pm.expect(responseData.id).to.exist.and.to.be.a('guid');", + "});", + "", + "", + "pm.test(\"Last name is a non-empty string\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.lastName).to.exist.and.to.be.a('string').and.to.have.lengthOf.at.least(1, \"Value should not be empty\");", + "});", + "", + "", + "pm.test(\"First mid name should be a non-empty string\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.firstMidName).to.be.a('string').and.to.have.lengthOf.at.least(1, \"Value should not be empty\");", + "});", + "", + "", + "pm.test(\"enrollmentDate is a valid date format\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('object');", + " pm.expect(responseData.enrollmentDate).to.match(/\\d{4}-\\d{2}-\\d{2}/);", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": "{{ServerUrl}}/student/{{CreatedId}}" + }, + "response": [] + }, + { + "name": "Upsert", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\":\"{{CreatedId}}\", \n \"lastName\": \"EvansEvans\",\n \"firstMidName\": \"Joseph\",\n \"enrollmentDate\": \"2023-09-07T10:55:29.825Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{ServerUrl}}/student" + }, + "response": [] + }, + { + "name": "GetAll", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "pm.test(\"Response is an array with at least one element\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('array');", + " pm.expect(responseData).to.have.lengthOf.at.least(1);", + "});", + "", + "", + "pm.test(\"Each element in the response array has the required fields\", function () {", + " const responseData = pm.response.json();", + " pm.expect(responseData).to.be.an('array');", + "", + " responseData.forEach(function (student) {", + " pm.expect(student.lastName).to.exist.and.to.be.a('string');", + " pm.expect(student.firstMidName).to.exist.and.to.be.a('string');", + " pm.expect(student.enrollmentDate).to.exist.and.to.be.a('string');", + " pm.expect(student.id).to.exist.and.to.be.a('string');", + " });", + "});", + "", + "", + "pm.test(\"enrollmentDate is in a valid date format\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('array').that.is.not.empty;", + "", + " responseData.forEach(function (student) {", + " pm.expect(student.enrollmentDate).to.match(/^\\d{4}-\\d{2}-\\d{2}$/);", + " });", + "});", + "", + "", + "pm.test(\"All ids in the response array are unique\", function () {", + " const responseData = pm.response.json();", + "", + " pm.expect(responseData).to.be.an('array');", + "", + " const ids = [];", + " responseData.forEach(function (student) {", + " pm.expect(student.id).to.exist;", + " pm.expect(ids).to.not.include(student.id, \"Duplicate id found\");", + " ids.push(student.id);", + " });", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ServerUrl}}/student?page=0&pageSize=100", + "host": [ + "{{ServerUrl}}" + ], + "path": [ + "student" + ], + "query": [ + { + "key": "page", + "value": "0" + }, + { + "key": "pageSize", + "value": "100" + } + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml new file mode 100644 index 0000000..f49d639 --- /dev/null +++ b/tests/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.9" +services: + postman: + image: postman/newman:latest + command: + run test_postman_collection.json -k --environment=localhost.postman_environment.json + -r cli,json ##htmlextra,cli,json + # --reporter-htmlextra-export="reports/Template-Integration-Test.html" + # --reporter-json-export="reports/Template-Integration-Test.json" + volumes: + - ./collections:/etc/newman + +networks: + bbt-development: + external: true diff --git a/workflow/process.bpmn b/workflow/process.bpmn new file mode 100644 index 0000000..b33476b --- /dev/null +++ b/workflow/process.bpmn @@ -0,0 +1,85 @@ + + + + + Flow_1ie08zg + + + Flow_1o9zfqu + + + + + + + Flow_1ie08zg + Flow_0ky5dp1 + + + + + + Flow_0z7und2 + Flow_1o9zfqu + + + + + + Flow_0ky5dp1 + Flow_0z7und2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow/simple.bpmn b/workflow/simple.bpmn new file mode 100644 index 0000000..a4d06e0 --- /dev/null +++ b/workflow/simple.bpmn @@ -0,0 +1,55 @@ + + + + + + Flow_1htatg9 + + + + Flow_034szy9 + + + + + Flow_034szy9 + Flow_1htatg9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow/user-register.bpmn b/workflow/user-register.bpmn new file mode 100644 index 0000000..5b106ad --- /dev/null +++ b/workflow/user-register.bpmn @@ -0,0 +1,233 @@ + + + + + + + + + + + + + Flow_0bnak6z + Flow_1wfmlog + + + + + + + + + + + Flow_18v4ao3 + Flow_0bnak6z + + + Flow_034szy9 + + + + + + + + + + Flow_034szy9 + Flow_1pmdmn7 + + + Flow_19feqjj + Flow_1ivg8gj + Flow_0lln9hd + + + Flow_1ivg8gj + Flow_18v4ao3 + + + + + Flow_0lln9hd + Flow_0tojokg + + + + + + + + + + + + Flow_0tojokg + Flow_1wfmlog + + + + + + + + + + + Flow_0xv95vv + Flow_19feqjj + + + + Flow_1pmdmn7 + Flow_0xv95vv + + + + + + Starter message, triggered by transition + + + + Moving flow to next state. Target state is defined as "default". Target state set to target state on transition record. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +