Changes in / [79ae621:81e1ed6]
- Files:
-
- 5 added
- 93 deleted
- 65 edited
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r79ae621 r81e1ed6 284 284 *.xsd.cs 285 285 .terraform 286 287 # mac os288 *.DS_Store -
src/Clients/Angular/finki-chattery/angular.json
r79ae621 r81e1ed6 98 98 "tsConfig": "tsconfig.spec.json", 99 99 "karmaConfig": "karma.conf.js", 100 "assets": [ 101 "src/favicon.ico", 102 "src/assets" 103 ], 104 "styles": [ 105 "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 106 "src/styles.scss" 107 ], 100 "assets": ["src/favicon.ico", "src/assets"], 101 "styles": ["./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.scss"], 108 102 "scripts": [] 109 103 } … … 112 106 "builder": "@angular-devkit/build-angular:tslint", 113 107 "options": { 114 "tsConfig": [ 115 "tsconfig.app.json", 116 "tsconfig.spec.json", 117 "e2e/tsconfig.json" 118 ], 119 "exclude": [ 120 "**/node_modules/**" 121 ] 108 "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"], 109 "exclude": ["**/node_modules/**"] 122 110 } 123 111 }, … … 137 125 } 138 126 }, 139 "defaultProject": "finki-chattery", 140 "cli": { 141 "analytics": false 142 } 127 "defaultProject": "finki-chattery" 143 128 } -
src/Clients/Angular/finki-chattery/src/app/app-routing.module.ts
r79ae621 r81e1ed6 1 1 import { NgModule } from '@angular/core'; 2 2 import { Routes, RouterModule } from '@angular/router'; 3 import { AuthCallbackComponent } from './auth-callback/auth-callback.component';4 import { AuthorizedGuard } from './core/guards/authorized.guard';5 3 6 4 const routes: Routes = [ 7 5 { 8 path: ' auth-callback',9 component: AuthCallbackComponent6 path: '**', 7 redirectTo: 'public/home' 10 8 }, 11 9 { 12 path: 'questioning', 13 canActivate: [AuthorizedGuard], 10 path: 'questions', 14 11 loadChildren: () => import('./modules/questioning/questioning.module').then((x) => x.QuestioningModule) 15 },16 {17 path: '**',18 redirectTo: 'questioning/preview'19 12 } 20 13 ]; -
src/Clients/Angular/finki-chattery/src/app/app.component.html
r79ae621 r81e1ed6 1 1 <main> 2 2 <mat-progress-bar class="global-loader" [class.hidden]="!(loader.isLoading | async)" mode="indeterminate"></mat-progress-bar> 3 <app-header></app-header>4 3 <router-outlet></router-outlet> 5 4 </main> -
src/Clients/Angular/finki-chattery/src/app/app.module.ts
r79ae621 r81e1ed6 10 10 import { CoreModule } from './core/core.module'; 11 11 import { translateConfiguration, TranslateFromJsonService } from './shared-app/services'; 12 import { AuthCallbackComponent } from './auth-callback/auth-callback.component';12 import { SharedMaterialModule } from './shared-material/shared-material.module'; 13 13 14 14 @NgModule({ 15 declarations: [AppComponent , AuthCallbackComponent],15 declarations: [AppComponent], 16 16 imports: [ 17 17 BrowserModule, 18 18 AppRoutingModule, 19 19 CoreModule, 20 SharedMaterialModule, 20 21 BrowserAnimationsModule, 21 22 ToastrModule.forRoot(), -
src/Clients/Angular/finki-chattery/src/app/core/core.module.ts
r79ae621 r81e1ed6 11 11 import { GUARDS } from './guards/guards'; 12 12 import { LoaderInterceptor } from './interceptors/loader.interceptor'; 13 import { SERVICES } from './services/services'; 13 14 import { reducers } from './state'; 14 15 import { TokenInterceptor } from './interceptors/token.interceptor'; 15 import { EffectsModule } from '@ngrx/effects';16 import { QuestionEffects } from './state/question-state/question.effects';17 import { CategoriesEffects } from './state/category-state/category.effects';18 16 19 17 @NgModule({ 20 18 declarations: [COMPONENTS], 21 19 providers: [ 20 SERVICES, 22 21 GUARDS, 23 22 { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true }, … … 34 33 maxAge: 25, 35 34 logOnly: !environment.production 36 }), 37 EffectsModule.forRoot([QuestionEffects, CategoriesEffects]) 35 }) 38 36 ], 39 exports: [HttpClientModule, SharedAppModule,COMPONENTS]37 exports: [HttpClientModule, COMPONENTS] 40 38 }) 41 39 export class CoreModule {} -
src/Clients/Angular/finki-chattery/src/app/core/interceptors/token.interceptor.ts
r79ae621 r81e1ed6 2 2 import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; 3 3 import { Observable } from 'rxjs'; 4 import { switchMap } from 'rxjs/operators'; 4 5 5 6 import { AuthService } from '../services'; … … 15 16 } 16 17 17 const requestToForward = request.clone({ 18 setHeaders: { Authorization: `Bearer ${this.auth.currentUserToken()}` } 19 }); 20 21 return next.handle(requestToForward); 18 return this.auth.currentUserToken().pipe( 19 switchMap((token) => { 20 const requestToForward = request.clone({ 21 setHeaders: { Authorization: `Bearer ${token}` } 22 }); 23 return next.handle(requestToForward); 24 }) 25 ); 22 26 } 23 27 } -
src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts
r79ae621 r81e1ed6 1 1 import { Injectable } from '@angular/core'; 2 import { User, UserManager } from 'oidc-client'; 3 import { Observable, of } from 'rxjs'; 2 import { UserManager } from 'oidc-client'; 3 import { Observable, from, of } from 'rxjs'; 4 import { map, switchMap } from 'rxjs/operators'; 4 5 5 6 import { environment } from '@env/environment'; … … 14 15 authority: environment.identityRoute, 15 16 client_id: environment.identityClientId, 16 redirect_uri: `${window.location.origin} /auth-callback`,17 redirect_uri: `${window.location.origin}`, 17 18 response_type: 'id_token token', 18 19 scope: 'openid app.api.finki-chattery profile', 19 20 post_logout_redirect_uri: window.location.origin 20 21 }); 21 22 public user: ApplicationUser | null = null;23 public oidcUser: User | null = null;24 22 25 23 constructor(private baseApi: BaseApiService) {} … … 33 31 } 34 32 35 public isLoggedIn(): boolean { 36 if (this.oidcUser) { 37 return !this.oidcUser.expired; 38 } 39 return false; 33 public isLoggedIn(): Observable<boolean> { 34 return from(this.userManager.getUser()).pipe( 35 map((user) => { 36 if (user) { 37 return true; 38 } 39 40 return false; 41 }) 42 ); 40 43 } 41 44 42 public currentUser(): ApplicationUser | null { 43 return this.user; 45 public currentUser(): Observable<ApplicationUser | null> { 46 return from(this.userManager.getUser()).pipe( 47 map((user) => { 48 if (!user) { 49 return null; 50 } 51 52 return new ApplicationUser( 53 user.profile.id, 54 user.profile.userType, 55 user.profile.emailAddress, 56 user.profile.username, 57 user.profile.isVerified 58 ); 59 }) 60 ); 44 61 } 45 62 46 public currentUserToken(): string { 47 if (this.oidcUser) { 48 return this.oidcUser.access_token; 49 } 63 public currentUserToken(): Observable<string> { 64 return from(this.userManager.getUser()).pipe( 65 map((user) => { 66 if (user?.access_token) { 67 return user.access_token; 68 } 50 69 51 return ''; 70 return ''; 71 }) 72 ); 52 73 } 53 74 54 75 public selfUserDto(): Observable<SelfUserResponse | null> { 55 if (this.isLoggedIn()) { 56 return this.baseApi.getSelfUser(); 57 } 58 return of(null); 76 return this.isLoggedIn().pipe( 77 switchMap((loggedIn) => { 78 if (loggedIn) { 79 return this.baseApi.getSelfUser(); 80 } 81 return of(null); 82 }) 83 ); 59 84 } 60 85 61 public async completeAuthentication(): Promise<void> { 62 return await this.userManager.signinRedirectCallback().then((user: User) => { 63 this.oidcUser = user; 64 this.user = new ApplicationUser( 65 user.profile.id, 66 user.profile.userType, 67 user.profile.emailAddress, 68 user.profile.username, 69 user.profile.isVerified 70 ); 71 }); 86 public signupCallback(): Observable<boolean> { 87 return from(this.userManager.signinRedirectCallback()).pipe(map((user) => user !== null)); 72 88 } 73 89 } -
src/Clients/Angular/finki-chattery/src/app/core/services/notification.service.ts
r79ae621 r81e1ed6 22 22 23 23 public successNotification(title: string, description?: string): void { 24 if (description) { 25 this.toastr.success(this.translate.instant(description), this.translate.instant(title)); 26 } 27 this.toastr.success(this.translate.instant(title)); 24 this.toastr.success(this.translate.instant(description), this.translate.instant(title)); 28 25 } 29 26 } -
src/Clients/Angular/finki-chattery/src/app/core/services/redirect.service.ts
r79ae621 r81e1ed6 1 1 import { Injectable } from '@angular/core'; 2 2 import { Router } from '@angular/router'; 3 import { switchMap } from 'rxjs/operators'; 3 4 4 5 import { ApplicationUserType } from 'src/app/shared-app/models'; … … 12 13 13 14 public redirectLoggedInUser(): void { 14 const currentUser = this.auth.user; 15 16 if (currentUser) { 17 switch (currentUser.userType) { 18 case ApplicationUserType.Student: 19 this.router.navigateByUrl(`questioning/preview`); 20 break; 21 case ApplicationUserType.Teacher: 22 break; 23 } 24 } 15 this.auth 16 .signupCallback() 17 .pipe(switchMap(() => this.auth.currentUser())) 18 .subscribe((currentUser) => { 19 if (currentUser) { 20 switch (currentUser.userType) { 21 case ApplicationUserType.Student: 22 break; 23 case ApplicationUserType.Teacher: 24 break; 25 } 26 } 27 }); 25 28 } 26 29 } -
src/Clients/Angular/finki-chattery/src/app/core/services/services.ts
r79ae621 r81e1ed6 1 export const SERVICES: any[] = []; -
src/Clients/Angular/finki-chattery/src/app/core/state/index.ts
r79ae621 r81e1ed6 1 1 import { ActionReducerMap } from '@ngrx/store'; 2 import { QuestionState } from './question-state/question.state';3 import { reducer as questionReducer } from './question-state/question.reducers';4 import { CategoryState } from './category-state/category.state';5 import { reducer as categoryReducer } from './category-state/category.reducers';6 2 7 export interface State { 8 question: QuestionState; 9 category: CategoryState; 10 } 3 export interface State {} 11 4 12 export const reducers: ActionReducerMap<State, any> = { 13 question: questionReducer, 14 category: categoryReducer 15 }; 5 export const reducers: ActionReducerMap<State, any> = {}; -
src/Clients/Angular/finki-chattery/src/app/modules/questioning/components/questioning-components.ts
r79ae621 r81e1ed6 1 import { QuestionPreviewGeneralComponent } from './question-preview-general/question-preview-general.component';2 import { QuestioningGeneralComponent } from './questioning-general/questioning-general.component';3 import { QuestionsPreviewGeneralComponent } from './questions-preview-general/questions-preview-general.component';4 import { QuestionsSearchComponent } from './questions-search/questions-search.component';5 1 import { AskQuestionComponent } from './ask-question/ask-question.component'; 6 2 7 export const QUESTIONING_COMPONENTS: any[] = [ 8 QuestionPreviewGeneralComponent, 9 QuestionsPreviewGeneralComponent, 10 QuestioningGeneralComponent, 11 QuestionsSearchComponent, 12 AskQuestionComponent 13 ]; 3 export const QUESTIONING_COMPONENTS: any[] = [AskQuestionComponent]; -
src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.module.ts
r79ae621 r81e1ed6 1 1 import { NgModule } from '@angular/core'; 2 2 3 import { QuestioningRoutingModule } from './questioning.routes';4 3 import { QUESTIONING_COMPONENTS } from './components/questioning-components'; 5 4 import { SharedAppModule } from 'src/app/shared-app/shared-app.module'; 5 import { QuestioningRoutingModule } from './questioning.routes'; 6 6 7 @NgModule({ 7 8 declarations: [QUESTIONING_COMPONENTS], -
src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.routes.ts
r79ae621 r81e1ed6 1 1 import { NgModule } from '@angular/core'; 2 2 import { Routes, RouterModule } from '@angular/router'; 3 import { QuestionPreviewGeneralComponent } from './components/question-preview-general/question-preview-general.component';4 import { QuestioningGeneralComponent } from './components/questioning-general/questioning-general.component';5 import { QuestionsPreviewGeneralComponent } from './components/questions-preview-general/questions-preview-general.component';6 import { QuestionsSearchComponent } from './components/questions-search/questions-search.component';7 3 import { AuthorizedStudentGuard } from 'src/app/core/guards/authorized-student.guard'; 8 4 import { AskQuestionComponent } from './components/ask-question/ask-question.component'; … … 10 6 const routes: Routes = [ 11 7 { 12 component: QuestioningGeneralComponent, 13 path: '', 14 children: [ 15 { 16 path: 'preview', 17 pathMatch: 'full', 18 component: QuestionsPreviewGeneralComponent 19 }, 20 { 21 path: 'search', 22 pathMatch: 'full', 23 component: QuestionsSearchComponent 24 }, 25 { 26 path: 'ask', 27 pathMatch: 'full', 28 component: AskQuestionComponent, 29 canActivate: [AuthorizedStudentGuard] 30 } 31 { 32 path: ':questionUid', 33 component: QuestionPreviewGeneralComponent 34 } 35 ] 8 path: 'ask', 9 component: AskQuestionComponent, 10 canActivate: [AuthorizedStudentGuard] 36 11 } 37 12 ]; -
src/Clients/Angular/finki-chattery/src/app/shared-app/directives/directives.ts
r79ae621 r81e1ed6 1 import { 2 HandleInputFormErrorsDirective, 3 HoverElevationDirective, 4 LoaderDirective, 5 HandleSelectFormErrorsDirective, 6 ShareLinkDirective 7 } from '.'; 1 import { HandleInputFormErrorsDirective, HoverElevationDirective, LoaderDirective, HandleSelectFormErrorsDirective } from '.'; 8 2 9 3 export const DIRECTIVES: any[] = [ … … 11 5 LoaderDirective, 12 6 HoverElevationDirective, 13 HandleSelectFormErrorsDirective, 14 ShareLinkDirective 7 HandleSelectFormErrorsDirective 15 8 ]; -
src/Clients/Angular/finki-chattery/src/app/shared-app/directives/index.ts
r79ae621 r81e1ed6 3 3 export * from './hover-elevation.directive'; 4 4 export * from './handle-select-form-errors.directive'; 5 export * from './share-link.directive'; -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/index.ts
r79ae621 r81e1ed6 1 1 export * from './error.models'; 2 2 export * from './user.models'; 3 export * from './question-state-view-models.models';4 export * from './category-state-view-models.models';5 export * from './question-state-enums.models';6 3 export * from './categories.models'; -
src/Clients/Angular/finki-chattery/src/app/shared-app/pipes/moment-date.pipe.ts
r79ae621 r81e1ed6 6 6 }) 7 7 export class MomentDatePipe implements PipeTransform { 8 transform(value: moment.Moment | undefined | null, dateFormat: string): any {8 transform(value: moment.Moment, dateFormat: string): any { 9 9 return moment(value).format(dateFormat); 10 10 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/services/translate-from-json.service.ts
r79ae621 r81e1ed6 43 43 } 44 44 45 public instant(key: string): string { 46 return this.translateService.instant(key); 45 public instant(key?: string): string | undefined { 46 if (key) { 47 return this.translateService.instant(key); 48 } 49 return undefined; 47 50 } 48 51 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/shared-app.module.ts
r79ae621 r81e1ed6 9 9 import { EditorModule } from '@tinymce/tinymce-angular'; 10 10 11 import { COMPONENTS } from './components/ components';11 import { COMPONENTS } from './components/generic/components'; 12 12 import { SharedMaterialModule } from '../shared-material/shared-material.module'; 13 13 import { DIRECTIVES } from './directives/directives'; 14 14 import { SERVICES } from './services/services'; 15 15 import { PIPES } from './pipes/pipes'; 16 import { FileUploadComponent } from './components/generic/file-upload/file-upload.component'; 17 import { HandleSelectFormErrorsDirective } from './directives/handle-select-form-errors.directive'; 16 18 17 19 @NgModule({ 18 declarations: [COMPONENTS, DIRECTIVES, PIPES ],20 declarations: [COMPONENTS, DIRECTIVES, PIPES, FileUploadComponent, HandleSelectFormErrorsDirective], 19 21 providers: [SERVICES], 20 22 imports: [ -
src/Clients/Angular/finki-chattery/src/app/shared-material/shared-material.module.ts
r79ae621 r81e1ed6 20 20 import { MatDatepickerModule } from '@angular/material/datepicker'; 21 21 import { MatNativeDateModule } from '@angular/material/core'; 22 import { MatChipsModule } from '@angular/material/chips';23 import { MatTooltipModule } from '@angular/material/tooltip';24 import { MatButtonToggleModule } from '@angular/material/button-toggle';25 import { MatBadgeModule } from '@angular/material/badge';26 27 22 @NgModule({ 28 23 imports: [ … … 45 40 MatTableModule, 46 41 MatDatepickerModule, 47 MatNativeDateModule, 48 MatChipsModule, 49 MatTooltipModule, 50 MatButtonToggleModule, 51 MatBadgeModule 42 MatNativeDateModule 52 43 ], 53 44 exports: [ … … 69 60 MatTableModule, 70 61 MatDatepickerModule, 71 MatNativeDateModule, 72 MatChipsModule, 73 MatTooltipModule, 74 MatButtonToggleModule, 75 MatBadgeModule 62 MatNativeDateModule 76 63 ] 77 64 }) -
src/Clients/Angular/finki-chattery/src/assets/translations/en.json
r79ae621 r81e1ed6 14 14 "password-not-match": "Passwords don't match", 15 15 "code-date-passed": "The code date has passed", 16 "not-found": "Not found", 17 "question-preview-subtitle": "Asked <b>{{createdOn}}</b>, Last active <b>{{lastActive}}</b>, Viewed <b>{{views}}</b> times", 18 "share-link": "Share", 19 "share-link-success": "Successfully copied link for sharing", 20 "question-asked-by-subtitle": "Asked on: {{date}}", 21 "question-answers": "{{answerCount}} Answers", 22 "question-answered-by-subtitle": "Answered on: {{date}}", 23 "student-reputation": "{{reputation}} reputation", 24 "vote-correct-answer": "This has been accepted as the correct answer by the owner of the question", 25 "answer-sort-oldest": "Oldest", 26 "answer-sort-votes": "Votes", 27 "internet-techologies": "Internet technologies", 28 "software-engineering": "Software engineering", 29 "visual-programming": "Visual programming", 30 "operating-systems": "Operating systems", 31 "internet-programming": "Internet programming", 32 "object-oriented-programming": "Object oriented programming", 33 "calculus": "Calculus", 34 "discrete-mathematics": "Discrete mathematics", 35 "web-programming": "Web programming", 36 "advanced-programming": "Advanced programming", 37 "questions-preview-find-question": "Search for a question", 38 "questions-preview-find-question-categories": "Search in categories", 39 "questions-search": "Search", 40 "questions-preview": "Preview questions", 41 "question-sort-newest": "Newest", 42 "question-sort-popular": "Most popular", 43 "questions-preview-question-subtitle": "Asked on: {{date}}", 44 "questions-preview-question-answers": "Answers", 45 "questions-preview-question-views": "Views", 46 "questions-search-title": "Search results for: {{searchQuery}}" 16 "not-found": "Not found" 47 17 } -
src/Clients/Angular/finki-chattery/src/styles.scss
r79ae621 r81e1ed6 264 264 265 265 .avatar-image { 266 width: 80px;267 height: 80px;266 width: 100px; 267 height: 100px; 268 268 display: block; 269 269 border-radius: 50%; … … 285 285 z-index: 1100 !important; 286 286 } 287 288 .full-width {289 width: 100%;290 } -
src/FinkiChattery/FinkiChattery.Api/Controllers/v1/QuestionsController.cs
r79ae621 r81e1ed6 1 1 using FinkiChattery.Api.ApplicationServices.Authentication; 2 using FinkiChattery.Api.ApplicationServices.Questioning;3 2 using FinkiChattery.Commands.Questioning; 4 3 using FinkiChattery.Common.Mediator.Interfaces; 5 4 using FinkiChattery.Contracts.Questioning; 6 using FinkiChattery.Queries.Questioning;7 5 using IdentityServer4.AccessTokenValidation; 8 6 using Microsoft.AspNetCore.Authorization; 9 7 using Microsoft.AspNetCore.Mvc; 10 using System;11 8 using System.Threading.Tasks; 12 9 … … 32 29 return Ok(); 33 30 } 34 35 [HttpGet("{questionUid:Guid}")]36 [Authorize]37 public async Task<IActionResult> GetQuestionState([FromRoute] Guid questionUid)38 {39 var questionDto = await MediatorService.SendQueryAsync(new GetQuestionStateQuery(questionUid));40 return Ok(questionDto.ToQuestionStateResponse());41 }42 43 [HttpGet("preview")]44 [Authorize]45 public async Task<IActionResult> PreviewQuestions([FromQuery] GetPreviewQuestionsOrderEnum order)46 {47 var questions = await MediatorService.SendQueryAsync(new GetPreviewQuestionsQuery(order));48 return Ok(questions.ToPreviewQuestionsResponse());49 }50 51 [HttpGet("search")]52 [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Student)]53 public async Task<IActionResult> SearchQuestions([FromQuery] string searchText, [FromQuery] string categories)54 {55 var questions = await MediatorService.SendQueryAsync(new SearchQuestionsQuery(searchText, categories));56 return Ok(questions.ToPreviewQuestionsResponse());57 }58 31 } 59 32 } -
src/FinkiChattery/FinkiChattery.Api/FinkiChattery.Api.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 … … 27 26 <ProjectReference Include="..\FinkiChattery.Contracts\FinkiChattery.Contracts.csproj" /> 28 27 <ProjectReference Include="..\FinkiChattery.Persistence\FinkiChattery.Persistence.csproj" /> 29 <ProjectReference Include="..\FinkiChattery.Queries\FinkiChattery.Queries.csproj" />30 28 </ItemGroup> 31 29 -
src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs
r79ae621 r81e1ed6 9 9 using FinkiChattery.Persistence.Models; 10 10 using FinkiChattery.Persistence.Repositories; 11 using FinkiChattery.Persistence.UnitOfWork;12 using FinkiChattery.Queries.Questioning;13 11 using Hangfire; 14 12 using Hangfire.SqlServer; … … 31 29 services.AddScoped<IEventService, EventService>(); 32 30 services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); 33 services.AddMediatR(typeof(AskQuestionCommand) , typeof(GetQuestionStateQuery));31 services.AddMediatR(typeof(AskQuestionCommand)); 34 32 } 35 33 36 34 public static void AddHangfireService(this IServiceCollection services, IConfiguration configuration) 37 35 { 38 string connectionString = "HangfireConnection";39 #if DEBUG_DOCKER40 connectionString = "HangfireConnectionDocker";41 #endif42 43 36 services.AddHangfire(x => 44 37 { 45 x.UseSqlServerStorage(configuration.GetConnectionString( connectionString), new SqlServerStorageOptions38 x.UseSqlServerStorage(configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions 46 39 { 47 40 CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), … … 107 100 } 108 101 109 public static void Add UnitOfWork(this IServiceCollection services)102 public static void AddRepos(this IServiceCollection services) 110 103 { 111 services.AddScoped<IUnitOfWork, UnitOfWork>(); 104 services.AddScoped<ICategoriesRepo, CategoriesRepo>(); 105 services.AddScoped<ITeamRepo, TeamRepo>(); 106 services.AddScoped<IQuestionRepo, QuestionRepo>(); 107 services.AddScoped<IStudentRepo, StudentRepo>(); 112 108 } 113 109 … … 147 143 services.AddScoped<IStorageService, AwsStorageService>();*/ 148 144 } 145 146 // TODO: ADD HANGFIRE AND SCAFOLD DB IN HANGFIREDB 149 147 } 150 148 -
src/FinkiChattery/FinkiChattery.Api/Startup.cs
r79ae621 r81e1ed6 35 35 services.AddOriginUrlSettings(); 36 36 services.AddCurrentUser(); 37 services.Add UnitOfWork();37 services.AddRepos(); 38 38 services.AddAwsClient(Configuration); 39 39 services.AddHangfireService(Configuration); 40 40 41 string connectionString = "DefaultConnection";42 #if DEBUG_DOCKER43 connectionString = "DefaultConnectionDocker";44 #endif45 46 41 services.AddDbContext<ApplicationDbContext>(options => 47 options.UseSqlServer(Configuration.GetConnectionString( connectionString)));42 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 48 43 49 44 services.AddControllers() -
src/FinkiChattery/FinkiChattery.Api/appsettings.Development.json
r79ae621 r81e1ed6 25 25 }, 26 26 "corsSettings": { 27 "allowedCorsOrigins": ["http://localhost:4200"] 27 "allowedCorsOrigins": [ 28 "http://localhost:4200" 29 ] 28 30 } 29 31 }, 30 32 "ConnectionStrings": { 31 33 "DefaultConnection": "data source=.;initial catalog=FinkiChattery;integrated security=True;application name=FinkiChattery Base App;Pooling=true;Min Pool Size=5;Max Pool Size=30;", 32 "HangfireConnection": "data source=.;initial catalog=FinkiChatteryHangfire;integrated security=True;application name=FinkiChattery Hangfire App;Pooling=true;Min Pool Size=5;Max Pool Size=30;", 33 "DefaultConnectionDocker": "Data Source=localhost;Initial Catalog=FinkiChattery;User ID=sa;Password=Asfa+032;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;Pooling=true;Min Pool Size=5;Max Pool Size=30;", 34 "HangfireConnectionDocker": "Data Source=localhost;Initial Catalog=FinkiChatteryHangfire;User ID=sa;Password=Asfa+032;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;Pooling=true;Min Pool Size=5;Max Pool Size=30;" 34 "HangfireConnection": "data source=.;initial catalog=FinkiChatteryHangfire;integrated security=True;application name=FinkiChattery Hangfire App;Pooling=true;Min Pool Size=5;Max Pool Size=30;" 35 35 } 36 36 } -
src/FinkiChattery/FinkiChattery.Commands/FinkiChattery.Commands.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 -
src/FinkiChattery/FinkiChattery.Commands/Questioning/AskQuestion/AskQuestionCommand.cs
r79ae621 r81e1ed6 1 1 using FinkiChattery.Common.Mediator.Contracs; 2 2 using FinkiChattery.Common.User; 3 using FinkiChattery.Persistence.Context; 3 4 using FinkiChattery.Persistence.Models; 4 using FinkiChattery.Persistence. UnitOfWork;5 using FinkiChattery.Persistence.Repositories; 5 6 using System; 6 7 using System.Collections.Generic; … … 26 27 public class AskQuestionHandler : ICommandHandler<AskQuestionCommand, Guid> 27 28 { 28 public AskQuestionHandler( IUnitOfWork unitOfWork, ICurrentUser currentUser)29 public AskQuestionHandler(ApplicationDbContext dbContext, ICategoriesRepo categoriesRepo, IStudentRepo studentRepo, ICurrentUser currentUser) 29 30 { 30 UnitOfWork = unitOfWork; 31 DbContext = dbContext; 32 CategoriesRepo = categoriesRepo; 33 StudentRepo = studentRepo; 31 34 CurrentUser = currentUser; 32 35 } 33 36 34 public IUnitOfWork UnitOfWork { get; } 37 public ApplicationDbContext DbContext { get; } 38 public ICategoriesRepo CategoriesRepo { get; } 39 public IStudentRepo StudentRepo { get; } 35 40 public ICurrentUser CurrentUser { get; } 36 41 37 42 public async Task<Guid> Handle(AskQuestionCommand request, CancellationToken cancellationToken) 38 43 { 39 var questionCategories = await UnitOfWork.Categories.GetCategories(request.Categories);40 var currentStudent = await UnitOfWork.Students.GetStudent(CurrentUser.Id);44 var questionCategories = await CategoriesRepo.GetCategories(request.Categories); 45 var currentStudent = await StudentRepo.GetStudent(CurrentUser.Id); 41 46 42 47 var questionDatabaseEntity = new Question() … … 55 60 } 56 61 57 UnitOfWork.Questions.Add(questionDatabaseEntity);58 await UnitOfWork.SaveAsync();62 DbContext.Questions.Add(questionDatabaseEntity); 63 await DbContext.SaveChangesAsync(); 59 64 return questionDatabaseEntity.Uid; 60 65 } -
src/FinkiChattery/FinkiChattery.Commands/Questioning/AskQuestion/AskQuestionValidator.cs
r79ae621 r81e1ed6 1 1 using FinkiChattery.Commands.Questioning.Validators; 2 using FinkiChattery.Persistence. UnitOfWork;2 using FinkiChattery.Persistence.Repositories; 3 3 using FluentValidation; 4 4 … … 7 7 public class AskQuestionValidator : AbstractValidator<AskQuestionCommand> 8 8 { 9 public AskQuestionValidator(I UnitOfWork unitOfWork)9 public AskQuestionValidator(ICategoriesRepo categoriesRepo) 10 10 { 11 11 RuleFor(x => x.Title).QuestionTitleValidate(); 12 12 RuleFor(x => x.Text).QuestionTextValidate(); 13 RuleFor(x => x.Categories).Cascade(CascadeMode.Stop).ListNotNull().SetValidator(new CategoriesUidsExist( unitOfWork));13 RuleFor(x => x.Categories).Cascade(CascadeMode.Stop).ListNotNull().SetValidator(new CategoriesUidsExist(categoriesRepo)); 14 14 } 15 15 } -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/CategoriesUidsExist.cs
r79ae621 r81e1ed6 1 1 using FinkiChattery.Persistence.Repositories; 2 using FinkiChattery.Persistence.UnitOfWork;3 2 using FluentValidation.Validators; 4 3 using System; … … 11 10 public class CategoriesUidsExist : AsyncValidatorBase 12 11 { 13 public CategoriesUidsExist(I UnitOfWork unitOfWork)12 public CategoriesUidsExist(ICategoriesRepo categoriesRepo) 14 13 { 15 UnitOfWork = unitOfWork;14 CategoriesRepo = categoriesRepo; 16 15 } 17 16 18 public I UnitOfWork UnitOfWork{ get; }17 public ICategoriesRepo CategoriesRepo { get; } 19 18 20 19 protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation) … … 22 21 var categoriesUids = (IEnumerable<Guid>)context.PropertyValue; 23 22 24 return await UnitOfWork.Categories.CategoriesExist(categoriesUids);23 return await CategoriesRepo.CategoriesExist(categoriesUids); 25 24 } 26 25 -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/TeamWithUidExist.cs
r79ae621 r81e1ed6 1 using FinkiChattery.Persistence. UnitOfWork;1 using FinkiChattery.Persistence.Repositories; 2 2 using FluentValidation.Validators; 3 3 using System; … … 9 9 public class TeamWithUidExist : AsyncValidatorBase 10 10 { 11 public TeamWithUidExist(I UnitOfWork unitOfWork)11 public TeamWithUidExist(ITeamRepo teamRepo) 12 12 { 13 UnitOfWork = unitOfWork;13 TeamRepo = teamRepo; 14 14 } 15 15 16 public I UnitOfWork UnitOfWork{ get; }16 public ITeamRepo TeamRepo { get; } 17 17 18 18 protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation) 19 19 { 20 20 var teamUid = (Guid)context.PropertyValue; 21 return await UnitOfWork.Teams.TeamWithUidExists(teamUid);21 return await TeamRepo.TeamWithUidExists(teamUid); 22 22 } 23 23 -
src/FinkiChattery/FinkiChattery.Common/FinkiChattery.Common.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 -
src/FinkiChattery/FinkiChattery.Common/Mediator/Interfaces/IMediatorService.cs
r79ae621 r81e1ed6 11 11 Task<TResponse> SendAsync<TResponse>(ICommand<TResponse> request); 12 12 13 Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request, CancellationToken cancellationToken);14 15 Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request);16 17 13 Task PublishAsync<TNotification>(TNotification notification) where TNotification : IEvent; 18 14 -
src/FinkiChattery/FinkiChattery.Common/Mediator/MediatorService.cs
r79ae621 r81e1ed6 37 37 await mediator.Publish(notification, default); 38 38 } 39 40 public async Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request, CancellationToken cancellationToken)41 {42 return await mediator.Send(request, cancellationToken);43 }44 45 public async Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request)46 {47 return await mediator.Send(request);48 }49 39 } 50 40 } -
src/FinkiChattery/FinkiChattery.Contracts/FinkiChattery.Contracts.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 -
src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj
r79ae621 r81e1ed6 1 <?xml version="1.0" encoding="utf-8"?>1 <?xml version="1.0" encoding="utf-8"?> 2 2 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> 3 3 <PropertyGroup> … … 55 55 <VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion> 56 56 </PropertyGroup> 57 <Import Condition="'$( NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />58 <Import Condition="'$( NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />57 <Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" /> 58 <Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" /> 59 59 <ItemGroup> 60 60 <Folder Include="Properties" /> … … 70 70 <Folder Include="dbo\Tables\Student" /> 71 71 <Folder Include="FullTextSearch" /> 72 <Folder Include="dbo\Tables\Answer" />73 <Folder Include="dbo\Tables\AnswerResponse" />74 <Folder Include="dbo\Tables\Question" />75 <Folder Include="dbo\Tables\QuestionCategory" />76 <Folder Include="Snapshots" />77 72 </ItemGroup> 78 73 <ItemGroup> 79 74 <Build Include="dbo\Tables\Moderator.sql" /> 80 75 <Build Include="dbo\Tables\Teacher.sql" /> 76 <Build Include="dbo\Tables\Question.sql" /> 81 77 <Build Include="dbo\Tables\StudentTeam.sql" /> 82 78 <Build Include="dbo\Tables\TeacherTeam.sql" /> 79 <Build Include="dbo\Tables\Answer.sql" /> 80 <Build Include="dbo\Tables\QuestionCategory.sql" /> 81 <Build Include="dbo\Tables\AnswerResponse.sql" /> 83 82 <Build Include="dbo\Tables\Upvote.sql" /> 84 83 <Build Include="dbo\Tables\User\AspNetRoleClaims.sql" /> … … 92 91 <Build Include="FullTextSearch\FullTextIndexQuestion.sql" /> 93 92 <Build Include="FullTextSearch\QuestionFullTextCatalog.sql" /> 94 <Build Include="dbo\Tables\Question\Question.sql" />95 <None Include="dbo\Tables\Question\Question.Debug.Seed.sql" />96 <Build Include="dbo\Tables\Answer\Answer.sql" />97 <None Include="dbo\Tables\Answer\Answer.Debug.Seed.sql" />98 <Build Include="dbo\Tables\AnswerResponse\AnswerResponse.sql" />99 <None Include="dbo\Tables\AnswerResponse\AnswerResponse.Debug.Seed.sql" />100 <Build Include="dbo\Tables\QuestionCategory\QuestionCategory.sql" />101 <None Include="dbo\Tables\QuestionCategory\QuestionCategory.Debug.Seed.sql" />102 93 </ItemGroup> 103 94 <ItemGroup> … … 106 97 <PreDeploy Include="dbo\Scripts\Script.PreDeployment.sql" /> 107 98 <None Include="FinkiChattery.Database.publish.xml" /> 108 <None Include="Snapshots\FinkiChattery.Database_20210922_17-47-58.dacpac" />109 99 </ItemGroup> 110 100 <ItemGroup> … … 130 120 </SqlCmdVariable> 131 121 </ItemGroup> 132 <ItemGroup>133 <RefactorLog Include="FinkiChattery.Database.refactorlog" />134 </ItemGroup>135 122 </Project> -
src/FinkiChattery/FinkiChattery.Database/FullTextSearch/FullTextIndexQuestion.sql
r79ae621 r81e1ed6 1 CREATE FULLTEXT INDEX ON [dbo].[Question] ([ Search])1 CREATE FULLTEXT INDEX ON [dbo].[Question] ([Title], [Text]) 2 2 KEY INDEX [PK_Question] ON [QuestionFullTextCatalog] 3 3 WITH (CHANGE_TRACKING AUTO, STOPLIST OFF) -
src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/PostDeploymentScripts/Debug.PostDeployment.sql
r79ae621 r81e1ed6 1 :r ./../../Tables/User/Seed/Users.Debug.Seed.sql 2 :r ./../../Tables/Category/Category.Seed.sql 3 :r ./../../Tables/Student/Student.Debug.Seed.sql 4 :r ./../../Tables/Question/Question.Debug.Seed.sql 5 :r ./../../Tables/Answer/Answer.Debug.Seed.sql 6 :r ./../../Tables/AnswerResponse/AnswerResponse.Debug.Seed.sql 7 :r ./../../Tables/QuestionCategory/QuestionCategory.Debug.Seed.sql 1 :r .\..\..\Tables\User\Seed\Users.Debug.Seed.sql 2 :r .\..\..\Tables\Category\Category.Seed.sql 3 :r .\..\..\Tables\Student\Student.Debug.Seed.sql -
src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/PostDeploymentScripts/Production.PostDeployment.sql
r79ae621 r81e1ed6 1 :r . /../../Tables/Category/Category.Seed.sql1 :r .\..\..\Tables\Category\Category.Seed.sql -
src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/Script.PostDeployment.sql
r79ae621 r81e1ed6 4 4 PRINT 'Deploying DEBUG scripts'; 5 5 END 6 :r . /PostDeploymentScripts/Debug.PostDeployment.sql6 :r .\PostDeploymentScripts\Debug.PostDeployment.sql 7 7 BEGIN --Run scripts 8 8 PRINT 'End deploying DEBUG scripts'; … … 15 15 PRINT 'Deploying PRODUCTION scripts' 16 16 END 17 :r . /PostDeploymentScripts/Production.PostDeployment.sql17 :r .\PostDeploymentScripts\Production.PostDeployment.sql 18 18 BEGIN --Run scripts 19 19 PRINT 'End deploying PRODUCTION scripts' -
src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/Script.PreDeployment.sql
r79ae621 r81e1ed6 4 4 PRINT 'Deploying DEBUG pre deployment scripts'; 5 5 END 6 :r . /PreDeploymentScripts/Debug.PreDeployment.sql6 :r .\PreDeploymentScripts\Debug.PreDeployment.sql 7 7 BEGIN --Run scripts 8 8 PRINT 'End deploying DEBUG pre deployment scripts'; … … 15 15 PRINT 'Deploying PRODUCTION pre deployment scripts' 16 16 END 17 :r . /PreDeploymentScripts/Production.PreDeployment.sql17 :r .\PreDeploymentScripts\Production.PreDeployment.sql 18 18 BEGIN --Run scripts 19 19 PRINT 'End deploying PRODUCTION pre deployment scripts' -
src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Category/Category.Seed.sql
r79ae621 r81e1ed6 10 10 (2, N'7d19a33f-d4a9-4498-8beb-07a5ce75d638', N'software-engineering'), 11 11 (3, N'8317013c-7eb8-4ca9-83c5-0342895e4061', N'visual-programming'), 12 (4, N'11d42b97-a343-499e-b3c6-13fbfd3785f4', N'internet-programming'), 13 (5, N'b2e4f54d-664e-4301-9642-9a6e9ef8df84', N'object-oriented-programming'), 14 (6, N'77146a56-48b3-4e29-910a-54b7449f8c2b', N'calculus'), 15 (7, N'c1a7bc19-34b5-4434-ad6b-f6e0fa90c946', N'discrete-mathematics'), 16 (8, N'9f324879-0e63-4d3d-a945-e88d290a23f1', N'advanced-programming'), 17 (9, N'6dd8f274-32ba-4b4b-8270-fea799e9b2e2', N'web-programming'), 18 (10, N'a4493d42-e01f-4092-a700-47a0847c8c4e', N'operating-systems') 12 (4, N'a4493d42-e01f-4092-a700-47a0847c8c4e', N'operating-systems') 19 13 ) 20 14 AS temp ([ID], [Uid], [Name]) -
src/FinkiChattery/FinkiChattery.HangfireDatabase/FinkiChattery.HangfireDatabase.sqlproj
r79ae621 r81e1ed6 1 <?xml version="1.0" encoding="utf-8"?>1 <?xml version="1.0" encoding="utf-8"?> 2 2 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> 3 3 <PropertyGroup> … … 55 55 <VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion> 56 56 </PropertyGroup> 57 <Import Condition="'$( NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />58 <Import Condition="'$( NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />57 <Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" /> 58 <Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" /> 59 59 <ItemGroup> 60 60 <Folder Include="Properties" /> … … 80 80 <None Include="FinkiChattery.HangfireDatabase.publish.xml" /> 81 81 </ItemGroup> 82 <Import Condition="'$(NetCoreBuild)' == 'true'" Project="$(NETCoreTargetsPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />83 <ItemGroup>84 <PackageReference Condition="'$(NetCoreBuild)' == 'true'" Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />85 </ItemGroup>86 <Target Name="BeforeBuild">87 <Delete Files="$(BaseIntermediateOutputPath)\project.assets.json" />88 </Target>89 82 </Project> -
src/FinkiChattery/FinkiChattery.Identity/FinkiChattery.Identity.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 -
src/FinkiChattery/FinkiChattery.Identity/Properties/launchSettings.json
r79ae621 r81e1ed6 9 9 }, 10 10 "profiles": { 11 "FinkiChattery.Identity": {12 "commandName": "IISExpress",13 "environmentVariables": {14 "ASPNETCORE_ENVIRONMENT": "Development"15 },16 "applicationUrl": "https://localhost:44301"17 },18 11 "SelfHost": { 19 12 "commandName": "IISExpress", -
src/FinkiChattery/FinkiChattery.Identity/Startup.cs
r79ae621 r81e1ed6 28 28 services.AddControllersWithViews(); 29 29 30 string connectionString = "DefaultConnection";31 #if DEBUG_DOCKER32 connectionString = "DefaultConnectionDocker";33 #endif34 35 30 services.AddDbContextPool<ApplicationDbContext<ApplicationUser>>(options => 36 options.UseSqlServer(Configuration.GetConnectionString( connectionString)));31 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 37 32 38 33 services.AddIdentityServerAndIdentityProvider(AppSettings); -
src/FinkiChattery/FinkiChattery.Identity/appsettings.Development.json
r79ae621 r81e1ed6 10 10 { 11 11 "allowedCorsOrigins": [ "http://localhost:4200" ], 12 "redirectUris": [ "http://localhost:4200 /auth-callback" ],12 "redirectUris": [ "http://localhost:4200" ], 13 13 "postLogoutRedirectUris": [ "http://localhost:4200" ] 14 14 } … … 17 17 }, 18 18 "ConnectionStrings": { 19 "DefaultConnection": "data source=.;initial catalog=FinkiChattery;integrated security=True;application name=FinkiChattery Base App;Pooling=true;Min Pool Size=5;Max Pool Size=30;", 20 "DefaultConnectionDocker": "Data Source=localhost;Initial Catalog=FinkiChattery;User ID=sa;Password=Asfa+032;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;Pooling=true;Min Pool Size=5;Max Pool Size=30;" 19 "DefaultConnection": "data source=.;initial catalog=FinkiChattery;integrated security=True;application name=FinkiChattery Base App;Pooling=true;Min Pool Size=5;Max Pool Size=30;" 21 20 } 22 21 } -
src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerConfig.cs
r79ae621 r81e1ed6 22 22 builder.Property(x => x.CorrectAnswer).HasColumnName(@"CorrectAnswer").HasColumnType("bit").IsRequired(); 23 23 builder.Property(x => x.CreatedOn).HasColumnName(@"CreatedOn").HasColumnType("smalldatetime").IsRequired(); 24 builder.Property(x => x.UpvotesCount).HasColumnName(@"UpvotesCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);25 24 26 25 builder.HasOne(x => x.Question).WithMany(x => x.Answers).HasForeignKey(x => x.QuestionFk).OnDelete(DeleteBehavior.Restrict); -
src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerResponseConfig.cs
r79ae621 r81e1ed6 28 28 29 29 builder.HasOne(x => x.Answer).WithMany(x => x.AnswerResponses).HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Restrict); 30 builder.HasOne(x => x.Student).WithMany().HasForeignKey(x => x. StudentFk).OnDelete(DeleteBehavior.Restrict);30 builder.HasOne(x => x.Student).WithMany().HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Restrict); 31 31 } 32 32 } -
src/FinkiChattery/FinkiChattery.Persistence/Configurations/QuestionCategoryConfig.cs
r79ae621 r81e1ed6 26 26 27 27 builder.HasOne(x => x.Question).WithMany(x => x.QuestionCategories).HasForeignKey(x => x.QuestionFk).OnDelete(DeleteBehavior.Restrict); 28 builder.HasOne(x => x.Category).WithMany().HasForeignKey(x => x. CategoryFk).OnDelete(DeleteBehavior.Restrict);28 builder.HasOne(x => x.Category).WithMany().HasForeignKey(x => x.QuestionFk).OnDelete(DeleteBehavior.Restrict); 29 29 } 30 30 } -
src/FinkiChattery/FinkiChattery.Persistence/Configurations/QuestionConfig.cs
r79ae621 r81e1ed6 24 24 builder.Property(x => x.Views).HasColumnName(@"Views").HasColumnType("bigint").IsRequired().HasDefaultValue(0); 25 25 builder.Property(x => x.LastActiveOn).HasColumnName(@"LastActiveOn").HasColumnType("smalldatetime").IsRequired(); 26 builder.Property(x => x.Search).HasColumnType(@"Search").HasColumnType("nvarchar").HasMaxLength(4000).IsRequired();27 builder.Property(x => x.AnswersCount).HasColumnType(@"AnswersCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);28 26 29 27 builder.HasOne(x => x.Student).WithMany(x => x.Questions).HasForeignKey(x => x.StudentFk).OnDelete(DeleteBehavior.NoAction); -
src/FinkiChattery/FinkiChattery.Persistence/FinkiChattery.Persistence.csproj
r79ae621 r81e1ed6 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 -
src/FinkiChattery/FinkiChattery.Persistence/Models/Answer.cs
r79ae621 r81e1ed6 20 20 public DateTime CreatedOn { get; set; } 21 21 22 public long UpvotesCount { get; set; }23 24 22 public virtual ICollection<Upvote> Upvotes { get; set; } 25 23 -
src/FinkiChattery/FinkiChattery.Persistence/Models/Question.cs
r79ae621 r81e1ed6 32 32 public DateTime LastActiveOn { get; set; } 33 33 34 public string Search { get; set; }35 36 public long AnswersCount { get; set; }37 38 34 public virtual ICollection<Answer> Answers { get; set; } 39 35 40 36 public virtual ICollection<QuestionCategory> QuestionCategories { get; set; } 37 38 // TODO: Pole po koe ke pravime queries 41 39 } 42 40 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/Repository.cs
r79ae621 r81e1ed6 8 8 namespace FinkiChattery.Persistence.Repositories 9 9 { 10 public abstract class Repository<T> : IRepository<T>where T : BaseEntity10 public abstract class Repository<T> where T : BaseEntity 11 11 { 12 12 public Repository(ApplicationDbContext dbContext) -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/ICategoriesRepo.cs
r79ae621 r81e1ed6 8 8 namespace FinkiChattery.Persistence.Repositories 9 9 { 10 public interface ICategoriesRepo : IRepository<Category>10 public interface ICategoriesRepo 11 11 { 12 12 public Task<bool> CategoriesExist(IEnumerable<Guid> categoriesUids); -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs
r79ae621 r81e1ed6 1 using FinkiChattery.Persistence.Models; 2 using FinkiChattery.Persistence.Repositories.Contracts; 3 using System; 4 using System.Collections.Generic; 5 using System.Threading.Tasks; 6 7 namespace FinkiChattery.Persistence.Repositories 1 namespace FinkiChattery.Persistence.Repositories 8 2 { 9 public interface IQuestionRepo : IRepository<Question>3 public interface IQuestionRepo 10 4 { 11 Task<List<QuestionPreviewDto>> SearchQuestions(string searchText, IEnumerable<Guid> categories);12 13 Task<QuestionStateDto> GetQuestionState(Guid questionUid);14 15 Task<List<QuestionPreviewDto>> GetPreviewQuestionsLatest();16 17 Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular();18 5 } 19 6 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs
r79ae621 r81e1ed6 4 4 namespace FinkiChattery.Persistence.Repositories 5 5 { 6 public interface IStudentRepo : IRepository<Student>6 public interface IStudentRepo 7 7 { 8 8 public Task<Student> GetStudent(long applicationUserFk); -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/ITeamRepo.cs
r79ae621 r81e1ed6 1 using FinkiChattery.Persistence.Models; 2 using System; 1 using System; 3 2 using System.Threading.Tasks; 4 3 5 4 namespace FinkiChattery.Persistence.Repositories 6 5 { 7 public interface ITeamRepo : IRepository<Team>6 public interface ITeamRepo 8 7 { 9 8 public Task<bool> TeamWithUidExists(Guid teamUid); -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs
r79ae621 r81e1ed6 1 1 using FinkiChattery.Persistence.Context; 2 2 using FinkiChattery.Persistence.Models; 3 using FinkiChattery.Persistence.Repositories.Contracts;4 using Microsoft.Data.SqlClient;5 using Microsoft.EntityFrameworkCore;6 using System;7 using System.Collections.Generic;8 using System.Linq;9 using System.Linq.Expressions;10 using System.Text.RegularExpressions;11 using System.Threading.Tasks;12 3 13 4 namespace FinkiChattery.Persistence.Repositories … … 18 9 { 19 10 } 20 21 public async Task<List<QuestionPreviewDto>> GetPreviewQuestionsLatest()22 {23 Expression<Func<Question, DateTime>> orderBy = x => x.CreatedOn;24 return await GetPreviewQuestions(orderBy);25 }26 27 public async Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular()28 {29 Expression<Func<Question, long>> orderBy = x => x.Views;30 return await GetPreviewQuestions(orderBy);31 }32 33 private async Task<List<QuestionPreviewDto>> GetPreviewQuestions<T>(Expression<Func<Question, T>> orderBy)34 {35 return await DbSet36 .AsNoTracking()37 .Include(x => x.QuestionCategories).ThenInclude(x => x.Category)38 .OrderByDescending(orderBy)39 .Select(x => new QuestionPreviewDto(x.Id,40 x.Uid,41 x.Title,42 x.Views,43 x.AnswersCount,44 x.CreatedOn,45 x.QuestionCategories.Select(y => new QuestionPreviewCategoryDto(y.Id, y.Uid, y.Category.Name))))46 .Skip(0).Take(30)47 .ToListAsync();48 }49 50 public async Task<QuestionStateDto> GetQuestionState(Guid questionUid)51 {52 // TODO: MAYBE WRITE THIS QUERY AS SP ??53 var questionDto = await DbSet54 .AsNoTracking()55 .Include(x => x.Student)56 .Include(x => x.Team)57 .Include(x => x.Answers).ThenInclude(y => y.Student)58 .Include(x => x.Answers).ThenInclude(y => y.AnswerResponses).ThenInclude(y => y.Student)59 .Include(x => x.QuestionCategories).ThenInclude(y => y.Category)60 .Where(x => x.Uid == questionUid)61 .Select(x => new QuestionStateDto(62 x.Id,63 x.Uid,64 x.Title,65 x.Text,66 x.CreatedOn,67 x.Views,68 x.LastActiveOn,69 new StudentQuestionStateDto(70 x.Student.Id,71 x.Student.Uid,72 x.Student.IndexNumber,73 x.Student.ImageUrl,74 x.Student.Reputation),75 x.Answers.Select(y =>76 new AnswerQuestionStateDto(77 y.Id,78 y.Uid,79 y.Text,80 y.CorrectAnswer,81 y.CreatedOn,82 y.UpvotesCount,83 new AnswerStudentQuestionStateDto(84 y.Student.Id,85 y.Student.Uid,86 y.Student.IndexNumber,87 y.Student.ImageUrl,88 y.Student.Reputation),89 y.AnswerResponses.Select(z =>90 new AnswerResponseQuestionStateDto(91 z.Id,92 z.Uid,93 z.Text,94 z.CreatedOn,95 new AnswerResponseStudentQuestionStateDto(96 z.Student.Id,97 z.Student.Uid,98 z.Student.IndexNumber,99 z.Student.ImageUrl,100 z.Student.Reputation))))),101 x.QuestionCategories.Select(y =>102 new QuestionCategoryQuestionStateDto(103 y.Id,104 y.Uid,105 y.Category.Name)),106 x.Team == null ? null : new TeamQuestionStateDto(107 x.Team.Id,108 x.Team.Uid,109 x.Team.Name)))110 .FirstOrDefaultAsync();111 112 return questionDto;113 }114 115 public async Task<List<QuestionPreviewDto>> SearchQuestions(string searchText, IEnumerable<Guid> categories)116 {117 var search = Regex.Replace(searchText, "[\\\\/:*?\"<>\\]\\[|&'`~^=%,(){}_\\-]", " ")118 .Split(" ".ToArray(), StringSplitOptions.RemoveEmptyEntries)119 .Select(c => $"\"{c}*\"");120 121 var searchString = string.Join(" AND ", search);122 123 var rawQuery = (IQueryable<Question>)124 DbSet.FromSqlRaw(@"125 SELECT [q].[Id], [q].[Uid], [q].[Title], [q].[Views], [q].[AnswersCount], [q].[CreatedOn]126 FROM [dbo].[Question] AS [q]127 INNER JOIN CONTAINSTABLE(Question, Search, @searchString, 30) ccontains ON [q].[Id] = ccontains.[KEY]",128 new SqlParameter("searchString", searchString))129 .Include(x => x.QuestionCategories).ThenInclude(x => x.Category);130 131 if (categories.Any())132 {133 rawQuery = rawQuery.Where(x => x.QuestionCategories.Any(y => categories.Contains(y.Category.Uid)));134 }135 136 return await rawQuery137 .Select(x => new QuestionPreviewDto(x.Id,138 x.Uid,139 x.Title,140 x.Views,141 x.AnswersCount,142 x.CreatedOn,143 x.QuestionCategories.Select(y => new QuestionPreviewCategoryDto(y.Id, y.Uid, y.Category.Name))))144 .ToListAsync();145 }146 11 } 147 12 } -
src/FinkiChattery/FinkiChattery.Queries/FinkiChattery.Queries.csproj
r79ae621 r81e1ed6 1 <Project Sdk="Microsoft.NET.Sdk">1 <Project Sdk="Microsoft.NET.Sdk"> 2 2 3 3 <PropertyGroup> 4 4 <TargetFramework>netcoreapp3.1</TargetFramework> 5 <Configurations>Debug;Release;Debug_Docker</Configurations>6 5 </PropertyGroup> 7 6 8 <ItemGroup>9 <ProjectReference Include="..\FinkiChattery.Common\FinkiChattery.Common.csproj" />10 <ProjectReference Include="..\FinkiChattery.Persistence\FinkiChattery.Persistence.csproj" />11 </ItemGroup>12 13 7 </Project> -
src/FinkiChattery/FinkiChattery.sln
r79ae621 r81e1ed6 22 22 Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "FinkiChattery.HangfireDatabase", "FinkiChattery.HangfireDatabase\FinkiChattery.HangfireDatabase.sqlproj", "{C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}" 23 23 EndProject 24 Project("{ 9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FinkiChattery.Commands", "FinkiChattery.Commands\FinkiChattery.Commands.csproj", "{7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}"24 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinkiChattery.Commands", "FinkiChattery.Commands\FinkiChattery.Commands.csproj", "{7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}" 25 25 EndProject 26 Project("{ 9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FinkiChattery.Queries", "FinkiChattery.Queries\FinkiChattery.Queries.csproj", "{D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}"26 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinkiChattery.Queries", "FinkiChattery.Queries\FinkiChattery.Queries.csproj", "{D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}" 27 27 EndProject 28 28 Global 29 29 GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 Debug_Docker|Any CPU = Debug_Docker|Any CPU31 30 Debug|Any CPU = Debug|Any CPU 32 31 Release|Any CPU = Release|Any CPU 33 32 EndGlobalSection 34 33 GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU36 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU37 34 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 35 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 36 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 37 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Release|Any CPU.Build.0 = Release|Any CPU 41 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU42 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU43 38 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 39 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 40 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 41 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Release|Any CPU.Build.0 = Release|Any CPU 47 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU48 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU49 42 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 43 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 44 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 45 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Release|Any CPU.Build.0 = Release|Any CPU 53 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU54 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU55 46 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 47 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 48 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 49 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Release|Any CPU.Build.0 = Release|Any CPU 59 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU60 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU61 50 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 51 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 52 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 53 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Release|Any CPU.Build.0 = Release|Any CPU 65 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU66 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU67 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug_Docker|Any CPU.Deploy.0 = Debug_Docker|Any CPU68 54 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 55 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug|Any CPU.Build.0 = Debug|Any CPU … … 72 58 {5E1219F5-FC7D-4138-811D-26934E946D30}.Release|Any CPU.Build.0 = Release|Any CPU 73 59 {5E1219F5-FC7D-4138-811D-26934E946D30}.Release|Any CPU.Deploy.0 = Release|Any CPU 74 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU75 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU76 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug_Docker|Any CPU.Deploy.0 = Debug_Docker|Any CPU77 60 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 78 61 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug|Any CPU.Build.0 = Debug|Any CPU … … 81 64 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Release|Any CPU.Build.0 = Release|Any CPU 82 65 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Release|Any CPU.Deploy.0 = Release|Any CPU 83 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU84 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU85 66 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 86 67 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug|Any CPU.Build.0 = Debug|Any CPU 87 68 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Release|Any CPU.ActiveCfg = Release|Any CPU 88 69 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Release|Any CPU.Build.0 = Release|Any CPU 89 {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU90 {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU91 70 {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 71 {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug|Any CPU.Build.0 = Debug|Any CPU
Note:
See TracChangeset
for help on using the changeset viewer.