Changes in / [79ae621:81e1ed6]


Ignore:
Files:
5 added
93 deleted
65 edited

Legend:

Unmodified
Added
Removed
  • .gitignore

    r79ae621 r81e1ed6  
    284284*.xsd.cs
    285285.terraform
    286 
    287 # mac os
    288 *.DS_Store
  • src/Clients/Angular/finki-chattery/angular.json

    r79ae621 r81e1ed6  
    9898            "tsConfig": "tsconfig.spec.json",
    9999            "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"],
    108102            "scripts": []
    109103          }
     
    112106          "builder": "@angular-devkit/build-angular:tslint",
    113107          "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/**"]
    122110          }
    123111        },
     
    137125    }
    138126  },
    139   "defaultProject": "finki-chattery",
    140   "cli": {
    141     "analytics": false
    142   }
     127  "defaultProject": "finki-chattery"
    143128}
  • src/Clients/Angular/finki-chattery/src/app/app-routing.module.ts

    r79ae621 r81e1ed6  
    11import { NgModule } from '@angular/core';
    22import { Routes, RouterModule } from '@angular/router';
    3 import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
    4 import { AuthorizedGuard } from './core/guards/authorized.guard';
    53
    64const routes: Routes = [
    75  {
    8     path: 'auth-callback',
    9     component: AuthCallbackComponent
     6    path: '**',
     7    redirectTo: 'public/home'
    108  },
    119  {
    12     path: 'questioning',
    13     canActivate: [AuthorizedGuard],
     10    path: 'questions',
    1411    loadChildren: () => import('./modules/questioning/questioning.module').then((x) => x.QuestioningModule)
    15   },
    16   {
    17     path: '**',
    18     redirectTo: 'questioning/preview'
    1912  }
    2013];
  • src/Clients/Angular/finki-chattery/src/app/app.component.html

    r79ae621 r81e1ed6  
    11<main>
    22  <mat-progress-bar class="global-loader" [class.hidden]="!(loader.isLoading | async)" mode="indeterminate"></mat-progress-bar>
    3   <app-header></app-header>
    43  <router-outlet></router-outlet>
    54</main>
  • src/Clients/Angular/finki-chattery/src/app/app.module.ts

    r79ae621 r81e1ed6  
    1010import { CoreModule } from './core/core.module';
    1111import { translateConfiguration, TranslateFromJsonService } from './shared-app/services';
    12 import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
     12import { SharedMaterialModule } from './shared-material/shared-material.module';
    1313
    1414@NgModule({
    15   declarations: [AppComponent, AuthCallbackComponent],
     15  declarations: [AppComponent],
    1616  imports: [
    1717    BrowserModule,
    1818    AppRoutingModule,
    1919    CoreModule,
     20    SharedMaterialModule,
    2021    BrowserAnimationsModule,
    2122    ToastrModule.forRoot(),
  • src/Clients/Angular/finki-chattery/src/app/core/core.module.ts

    r79ae621 r81e1ed6  
    1111import { GUARDS } from './guards/guards';
    1212import { LoaderInterceptor } from './interceptors/loader.interceptor';
     13import { SERVICES } from './services/services';
    1314import { reducers } from './state';
    1415import { 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';
    1816
    1917@NgModule({
    2018  declarations: [COMPONENTS],
    2119  providers: [
     20    SERVICES,
    2221    GUARDS,
    2322    { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true },
     
    3433      maxAge: 25,
    3534      logOnly: !environment.production
    36     }),
    37     EffectsModule.forRoot([QuestionEffects, CategoriesEffects])
     35    })
    3836  ],
    39   exports: [HttpClientModule, SharedAppModule, COMPONENTS]
     37  exports: [HttpClientModule, COMPONENTS]
    4038})
    4139export class CoreModule {}
  • src/Clients/Angular/finki-chattery/src/app/core/interceptors/token.interceptor.ts

    r79ae621 r81e1ed6  
    22import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
    33import { Observable } from 'rxjs';
     4import { switchMap } from 'rxjs/operators';
    45
    56import { AuthService } from '../services';
     
    1516    }
    1617
    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    );
    2226  }
    2327}
  • src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts

    r79ae621 r81e1ed6  
    11import { Injectable } from '@angular/core';
    2 import { User, UserManager } from 'oidc-client';
    3 import { Observable, of } from 'rxjs';
     2import { UserManager } from 'oidc-client';
     3import { Observable, from, of } from 'rxjs';
     4import { map, switchMap } from 'rxjs/operators';
    45
    56import { environment } from '@env/environment';
     
    1415    authority: environment.identityRoute,
    1516    client_id: environment.identityClientId,
    16     redirect_uri: `${window.location.origin}/auth-callback`,
     17    redirect_uri: `${window.location.origin}`,
    1718    response_type: 'id_token token',
    1819    scope: 'openid app.api.finki-chattery profile',
    1920    post_logout_redirect_uri: window.location.origin
    2021  });
    21 
    22   public user: ApplicationUser | null = null;
    23   public oidcUser: User | null = null;
    2422
    2523  constructor(private baseApi: BaseApiService) {}
     
    3331  }
    3432
    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    );
    4043  }
    4144
    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    );
    4461  }
    4562
    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        }
    5069
    51     return '';
     70        return '';
     71      })
     72    );
    5273  }
    5374
    5475  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    );
    5984  }
    6085
    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));
    7288  }
    7389}
  • src/Clients/Angular/finki-chattery/src/app/core/services/notification.service.ts

    r79ae621 r81e1ed6  
    2222
    2323  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));
    2825  }
    2926}
  • src/Clients/Angular/finki-chattery/src/app/core/services/redirect.service.ts

    r79ae621 r81e1ed6  
    11import { Injectable } from '@angular/core';
    22import { Router } from '@angular/router';
     3import { switchMap } from 'rxjs/operators';
    34
    45import { ApplicationUserType } from 'src/app/shared-app/models';
     
    1213
    1314  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      });
    2528  }
    2629}
  • src/Clients/Angular/finki-chattery/src/app/core/services/services.ts

    r79ae621 r81e1ed6  
     1export const SERVICES: any[] = [];
  • src/Clients/Angular/finki-chattery/src/app/core/state/index.ts

    r79ae621 r81e1ed6  
    11import { 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';
    62
    7 export interface State {
    8   question: QuestionState;
    9   category: CategoryState;
    10 }
     3export interface State {}
    114
    12 export const reducers: ActionReducerMap<State, any> = {
    13   question: questionReducer,
    14   category: categoryReducer
    15 };
     5export 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';
    51import { AskQuestionComponent } from './ask-question/ask-question.component';
    62
    7 export const QUESTIONING_COMPONENTS: any[] = [
    8   QuestionPreviewGeneralComponent,
    9   QuestionsPreviewGeneralComponent,
    10   QuestioningGeneralComponent,
    11   QuestionsSearchComponent,
    12   AskQuestionComponent
    13 ];
     3export const QUESTIONING_COMPONENTS: any[] = [AskQuestionComponent];
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.module.ts

    r79ae621 r81e1ed6  
    11import { NgModule } from '@angular/core';
    22
    3 import { QuestioningRoutingModule } from './questioning.routes';
    43import { QUESTIONING_COMPONENTS } from './components/questioning-components';
    54import { SharedAppModule } from 'src/app/shared-app/shared-app.module';
     5import { QuestioningRoutingModule } from './questioning.routes';
     6
    67@NgModule({
    78  declarations: [QUESTIONING_COMPONENTS],
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.routes.ts

    r79ae621 r81e1ed6  
    11import { NgModule } from '@angular/core';
    22import { 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';
    73import { AuthorizedStudentGuard } from 'src/app/core/guards/authorized-student.guard';
    84import { AskQuestionComponent } from './components/ask-question/ask-question.component';
     
    106const routes: Routes = [
    117  {
    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]
    3611  }
    3712];
  • 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 '.';
     1import { HandleInputFormErrorsDirective, HoverElevationDirective, LoaderDirective, HandleSelectFormErrorsDirective } from '.';
    82
    93export const DIRECTIVES: any[] = [
     
    115  LoaderDirective,
    126  HoverElevationDirective,
    13   HandleSelectFormErrorsDirective,
    14   ShareLinkDirective
     7  HandleSelectFormErrorsDirective
    158];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/directives/index.ts

    r79ae621 r81e1ed6  
    33export * from './hover-elevation.directive';
    44export * 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  
    11export * from './error.models';
    22export * 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';
    63export * from './categories.models';
  • src/Clients/Angular/finki-chattery/src/app/shared-app/pipes/moment-date.pipe.ts

    r79ae621 r81e1ed6  
    66})
    77export class MomentDatePipe implements PipeTransform {
    8   transform(value: moment.Moment | undefined | null, dateFormat: string): any {
     8  transform(value: moment.Moment, dateFormat: string): any {
    99    return moment(value).format(dateFormat);
    1010  }
  • src/Clients/Angular/finki-chattery/src/app/shared-app/services/translate-from-json.service.ts

    r79ae621 r81e1ed6  
    4343  }
    4444
    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;
    4750  }
    4851}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/shared-app.module.ts

    r79ae621 r81e1ed6  
    99import { EditorModule } from '@tinymce/tinymce-angular';
    1010
    11 import { COMPONENTS } from './components/components';
     11import { COMPONENTS } from './components/generic/components';
    1212import { SharedMaterialModule } from '../shared-material/shared-material.module';
    1313import { DIRECTIVES } from './directives/directives';
    1414import { SERVICES } from './services/services';
    1515import { PIPES } from './pipes/pipes';
     16import { FileUploadComponent } from './components/generic/file-upload/file-upload.component';
     17import { HandleSelectFormErrorsDirective } from './directives/handle-select-form-errors.directive';
    1618
    1719@NgModule({
    18   declarations: [COMPONENTS, DIRECTIVES, PIPES],
     20  declarations: [COMPONENTS, DIRECTIVES, PIPES, FileUploadComponent, HandleSelectFormErrorsDirective],
    1921  providers: [SERVICES],
    2022  imports: [
  • src/Clients/Angular/finki-chattery/src/app/shared-material/shared-material.module.ts

    r79ae621 r81e1ed6  
    2020import { MatDatepickerModule } from '@angular/material/datepicker';
    2121import { 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 
    2722@NgModule({
    2823  imports: [
     
    4540    MatTableModule,
    4641    MatDatepickerModule,
    47     MatNativeDateModule,
    48     MatChipsModule,
    49     MatTooltipModule,
    50     MatButtonToggleModule,
    51     MatBadgeModule
     42    MatNativeDateModule
    5243  ],
    5344  exports: [
     
    6960    MatTableModule,
    7061    MatDatepickerModule,
    71     MatNativeDateModule,
    72     MatChipsModule,
    73     MatTooltipModule,
    74     MatButtonToggleModule,
    75     MatBadgeModule
     62    MatNativeDateModule
    7663  ]
    7764})
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r79ae621 r81e1ed6  
    1414  "password-not-match": "Passwords don't match",
    1515  "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"
    4717}
  • src/Clients/Angular/finki-chattery/src/styles.scss

    r79ae621 r81e1ed6  
    264264
    265265.avatar-image {
    266   width: 80px;
    267   height: 80px;
     266  width: 100px;
     267  height: 100px;
    268268  display: block;
    269269  border-radius: 50%;
     
    285285  z-index: 1100 !important;
    286286}
    287 
    288 .full-width {
    289   width: 100%;
    290 }
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/QuestionsController.cs

    r79ae621 r81e1ed6  
    11using FinkiChattery.Api.ApplicationServices.Authentication;
    2 using FinkiChattery.Api.ApplicationServices.Questioning;
    32using FinkiChattery.Commands.Questioning;
    43using FinkiChattery.Common.Mediator.Interfaces;
    54using FinkiChattery.Contracts.Questioning;
    6 using FinkiChattery.Queries.Questioning;
    75using IdentityServer4.AccessTokenValidation;
    86using Microsoft.AspNetCore.Authorization;
    97using Microsoft.AspNetCore.Mvc;
    10 using System;
    118using System.Threading.Tasks;
    129
     
    3229            return Ok();
    3330        }
    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         }
    5831    }
    5932}
  • src/FinkiChattery/FinkiChattery.Api/FinkiChattery.Api.csproj

    r79ae621 r81e1ed6  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
     
    2726    <ProjectReference Include="..\FinkiChattery.Contracts\FinkiChattery.Contracts.csproj" />
    2827    <ProjectReference Include="..\FinkiChattery.Persistence\FinkiChattery.Persistence.csproj" />
    29     <ProjectReference Include="..\FinkiChattery.Queries\FinkiChattery.Queries.csproj" />
    3028  </ItemGroup>
    3129
  • src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs

    r79ae621 r81e1ed6  
    99using FinkiChattery.Persistence.Models;
    1010using FinkiChattery.Persistence.Repositories;
    11 using FinkiChattery.Persistence.UnitOfWork;
    12 using FinkiChattery.Queries.Questioning;
    1311using Hangfire;
    1412using Hangfire.SqlServer;
     
    3129            services.AddScoped<IEventService, EventService>();
    3230            services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
    33             services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery));
     31            services.AddMediatR(typeof(AskQuestionCommand));
    3432        }
    3533
    3634        public static void AddHangfireService(this IServiceCollection services, IConfiguration configuration)
    3735        {
    38             string connectionString = "HangfireConnection";
    39 #if DEBUG_DOCKER
    40             connectionString = "HangfireConnectionDocker";
    41 #endif
    42 
    4336            services.AddHangfire(x =>
    4437            {
    45                 x.UseSqlServerStorage(configuration.GetConnectionString(connectionString), new SqlServerStorageOptions
     38                x.UseSqlServerStorage(configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
    4639                {
    4740                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
     
    107100        }
    108101
    109         public static void AddUnitOfWork(this IServiceCollection services)
     102        public static void AddRepos(this IServiceCollection services)
    110103        {
    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>();
    112108        }
    113109
     
    147143                        services.AddScoped<IStorageService, AwsStorageService>();*/
    148144        }
     145
     146        // TODO: ADD HANGFIRE AND SCAFOLD DB IN HANGFIREDB
    149147    }
    150148
  • src/FinkiChattery/FinkiChattery.Api/Startup.cs

    r79ae621 r81e1ed6  
    3535            services.AddOriginUrlSettings();
    3636            services.AddCurrentUser();
    37             services.AddUnitOfWork();
     37            services.AddRepos();
    3838            services.AddAwsClient(Configuration);
    3939            services.AddHangfireService(Configuration);
    4040
    41             string connectionString = "DefaultConnection";
    42 #if DEBUG_DOCKER
    43             connectionString = "DefaultConnectionDocker";
    44 #endif
    45 
    4641            services.AddDbContext<ApplicationDbContext>(options =>
    47                 options.UseSqlServer(Configuration.GetConnectionString(connectionString)));
     42                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    4843
    4944            services.AddControllers()
  • src/FinkiChattery/FinkiChattery.Api/appsettings.Development.json

    r79ae621 r81e1ed6  
    2525    },
    2626    "corsSettings": {
    27       "allowedCorsOrigins": ["http://localhost:4200"]
     27      "allowedCorsOrigins": [
     28        "http://localhost:4200"
     29      ]
    2830    }
    2931  },
    3032  "ConnectionStrings": {
    3133    "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;"
    3535  }
    3636}
  • src/FinkiChattery/FinkiChattery.Commands/FinkiChattery.Commands.csproj

    r79ae621 r81e1ed6  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/AskQuestion/AskQuestionCommand.cs

    r79ae621 r81e1ed6  
    11using FinkiChattery.Common.Mediator.Contracs;
    22using FinkiChattery.Common.User;
     3using FinkiChattery.Persistence.Context;
    34using FinkiChattery.Persistence.Models;
    4 using FinkiChattery.Persistence.UnitOfWork;
     5using FinkiChattery.Persistence.Repositories;
    56using System;
    67using System.Collections.Generic;
     
    2627    public class AskQuestionHandler : ICommandHandler<AskQuestionCommand, Guid>
    2728    {
    28         public AskQuestionHandler(IUnitOfWork unitOfWork, ICurrentUser currentUser)
     29        public AskQuestionHandler(ApplicationDbContext dbContext, ICategoriesRepo categoriesRepo, IStudentRepo studentRepo, ICurrentUser currentUser)
    2930        {
    30             UnitOfWork = unitOfWork;
     31            DbContext = dbContext;
     32            CategoriesRepo = categoriesRepo;
     33            StudentRepo = studentRepo;
    3134            CurrentUser = currentUser;
    3235        }
    3336
    34         public IUnitOfWork UnitOfWork { get; }
     37        public ApplicationDbContext DbContext { get; }
     38        public ICategoriesRepo CategoriesRepo { get; }
     39        public IStudentRepo StudentRepo { get; }
    3540        public ICurrentUser CurrentUser { get; }
    3641
    3742        public async Task<Guid> Handle(AskQuestionCommand request, CancellationToken cancellationToken)
    3843        {
    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);
    4146
    4247            var questionDatabaseEntity = new Question()
     
    5560            }
    5661
    57             UnitOfWork.Questions.Add(questionDatabaseEntity);
    58             await UnitOfWork.SaveAsync();
     62            DbContext.Questions.Add(questionDatabaseEntity);
     63            await DbContext.SaveChangesAsync();
    5964            return questionDatabaseEntity.Uid;
    6065        }
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/AskQuestion/AskQuestionValidator.cs

    r79ae621 r81e1ed6  
    11using FinkiChattery.Commands.Questioning.Validators;
    2 using FinkiChattery.Persistence.UnitOfWork;
     2using FinkiChattery.Persistence.Repositories;
    33using FluentValidation;
    44
     
    77    public class AskQuestionValidator : AbstractValidator<AskQuestionCommand>
    88    {
    9         public AskQuestionValidator(IUnitOfWork unitOfWork)
     9        public AskQuestionValidator(ICategoriesRepo categoriesRepo)
    1010        {
    1111            RuleFor(x => x.Title).QuestionTitleValidate();
    1212            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));
    1414        }
    1515    }
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/CategoriesUidsExist.cs

    r79ae621 r81e1ed6  
    11using FinkiChattery.Persistence.Repositories;
    2 using FinkiChattery.Persistence.UnitOfWork;
    32using FluentValidation.Validators;
    43using System;
     
    1110    public class CategoriesUidsExist : AsyncValidatorBase
    1211    {
    13         public CategoriesUidsExist(IUnitOfWork unitOfWork)
     12        public CategoriesUidsExist(ICategoriesRepo categoriesRepo)
    1413        {
    15             UnitOfWork = unitOfWork;
     14            CategoriesRepo = categoriesRepo;
    1615        }
    1716
    18         public IUnitOfWork UnitOfWork { get; }
     17        public ICategoriesRepo CategoriesRepo { get; }
    1918
    2019        protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation)
     
    2221            var categoriesUids = (IEnumerable<Guid>)context.PropertyValue;
    2322
    24             return await UnitOfWork.Categories.CategoriesExist(categoriesUids);
     23            return await CategoriesRepo.CategoriesExist(categoriesUids);
    2524        }
    2625
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/TeamWithUidExist.cs

    r79ae621 r81e1ed6  
    1 using FinkiChattery.Persistence.UnitOfWork;
     1using FinkiChattery.Persistence.Repositories;
    22using FluentValidation.Validators;
    33using System;
     
    99    public class TeamWithUidExist : AsyncValidatorBase
    1010    {
    11         public TeamWithUidExist(IUnitOfWork unitOfWork)
     11        public TeamWithUidExist(ITeamRepo teamRepo)
    1212        {
    13             UnitOfWork = unitOfWork;
     13            TeamRepo = teamRepo;
    1414        }
    1515
    16         public IUnitOfWork UnitOfWork { get; }
     16        public ITeamRepo TeamRepo { get; }
    1717
    1818        protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation)
    1919        {
    2020            var teamUid = (Guid)context.PropertyValue;
    21             return await UnitOfWork.Teams.TeamWithUidExists(teamUid);
     21            return await TeamRepo.TeamWithUidExists(teamUid);       
    2222        }
    2323
  • src/FinkiChattery/FinkiChattery.Common/FinkiChattery.Common.csproj

    r79ae621 r81e1ed6  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
  • src/FinkiChattery/FinkiChattery.Common/Mediator/Interfaces/IMediatorService.cs

    r79ae621 r81e1ed6  
    1111        Task<TResponse> SendAsync<TResponse>(ICommand<TResponse> request);
    1212
    13         Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request, CancellationToken cancellationToken);
    14 
    15         Task<TResponse> SendQueryAsync<TResponse>(IQuery<TResponse> request);
    16 
    1713        Task PublishAsync<TNotification>(TNotification notification) where TNotification : IEvent;
    1814
  • src/FinkiChattery/FinkiChattery.Common/Mediator/MediatorService.cs

    r79ae621 r81e1ed6  
    3737            await mediator.Publish(notification, default);
    3838        }
    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         }
    4939    }
    5040}
  • src/FinkiChattery/FinkiChattery.Contracts/FinkiChattery.Contracts.csproj

    r79ae621 r81e1ed6  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
  • 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"?>
    22<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
    33  <PropertyGroup>
     
    5555    <VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
    5656  </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" />
    5959  <ItemGroup>
    6060    <Folder Include="Properties" />
     
    7070    <Folder Include="dbo\Tables\Student" />
    7171    <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" />
    7772  </ItemGroup>
    7873  <ItemGroup>
    7974    <Build Include="dbo\Tables\Moderator.sql" />
    8075    <Build Include="dbo\Tables\Teacher.sql" />
     76    <Build Include="dbo\Tables\Question.sql" />
    8177    <Build Include="dbo\Tables\StudentTeam.sql" />
    8278    <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" />
    8382    <Build Include="dbo\Tables\Upvote.sql" />
    8483    <Build Include="dbo\Tables\User\AspNetRoleClaims.sql" />
     
    9291    <Build Include="FullTextSearch\FullTextIndexQuestion.sql" />
    9392    <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" />
    10293  </ItemGroup>
    10394  <ItemGroup>
     
    10697    <PreDeploy Include="dbo\Scripts\Script.PreDeployment.sql" />
    10798    <None Include="FinkiChattery.Database.publish.xml" />
    108     <None Include="Snapshots\FinkiChattery.Database_20210922_17-47-58.dacpac" />
    10999  </ItemGroup>
    110100  <ItemGroup>
     
    130120    </SqlCmdVariable>
    131121  </ItemGroup>
    132   <ItemGroup>
    133     <RefactorLog Include="FinkiChattery.Database.refactorlog" />
    134   </ItemGroup>
    135122</Project>
  • src/FinkiChattery/FinkiChattery.Database/FullTextSearch/FullTextIndexQuestion.sql

    r79ae621 r81e1ed6  
    1 CREATE FULLTEXT INDEX ON [dbo].[Question] ([Search])
     1CREATE FULLTEXT INDEX ON [dbo].[Question] ([Title], [Text])
    22KEY INDEX [PK_Question] ON [QuestionFullTextCatalog]
    33WITH (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.sql
     1:r .\..\..\Tables\Category\Category.Seed.sql
  • src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/Script.PostDeployment.sql

    r79ae621 r81e1ed6  
    44                PRINT 'Deploying DEBUG scripts';
    55        END
    6         :r ./PostDeploymentScripts/Debug.PostDeployment.sql
     6        :r .\PostDeploymentScripts\Debug.PostDeployment.sql
    77        BEGIN --Run scripts
    88                PRINT 'End deploying DEBUG scripts';
     
    1515                PRINT 'Deploying PRODUCTION scripts'
    1616        END
    17                 :r ./PostDeploymentScripts/Production.PostDeployment.sql
     17                :r .\PostDeploymentScripts\Production.PostDeployment.sql
    1818                BEGIN --Run scripts
    1919                PRINT 'End deploying PRODUCTION scripts'
  • src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/Script.PreDeployment.sql

    r79ae621 r81e1ed6  
    44                PRINT 'Deploying DEBUG pre deployment scripts';
    55        END
    6         :r ./PreDeploymentScripts/Debug.PreDeployment.sql
     6        :r .\PreDeploymentScripts\Debug.PreDeployment.sql
    77        BEGIN --Run scripts
    88                PRINT 'End deploying DEBUG pre deployment scripts';
     
    1515                PRINT 'Deploying PRODUCTION pre deployment scripts'
    1616        END
    17         :r ./PreDeploymentScripts/Production.PreDeployment.sql
     17        :r .\PreDeploymentScripts\Production.PreDeployment.sql
    1818        BEGIN --Run scripts
    1919                PRINT 'End deploying PRODUCTION pre deployment scripts'
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Category/Category.Seed.sql

    r79ae621 r81e1ed6  
    1010                (2, N'7d19a33f-d4a9-4498-8beb-07a5ce75d638',  N'software-engineering'),
    1111                (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')
    1913                )
    2014                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"?>
    22<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
    33  <PropertyGroup>
     
    5555    <VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
    5656  </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" />
    5959  <ItemGroup>
    6060    <Folder Include="Properties" />
     
    8080    <None Include="FinkiChattery.HangfireDatabase.publish.xml" />
    8181  </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>
    8982</Project>
  • src/FinkiChattery/FinkiChattery.Identity/FinkiChattery.Identity.csproj

    r79ae621 r81e1ed6  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
  • src/FinkiChattery/FinkiChattery.Identity/Properties/launchSettings.json

    r79ae621 r81e1ed6  
    99  },
    1010  "profiles": {
    11     "FinkiChattery.Identity": {
    12       "commandName": "IISExpress",
    13       "environmentVariables": {
    14         "ASPNETCORE_ENVIRONMENT": "Development"
    15       },
    16       "applicationUrl": "https://localhost:44301"
    17     },
    1811    "SelfHost": {
    1912      "commandName": "IISExpress",
  • src/FinkiChattery/FinkiChattery.Identity/Startup.cs

    r79ae621 r81e1ed6  
    2828            services.AddControllersWithViews();
    2929
    30             string connectionString = "DefaultConnection";
    31 #if DEBUG_DOCKER
    32             connectionString = "DefaultConnectionDocker";
    33 #endif
    34 
    3530            services.AddDbContextPool<ApplicationDbContext<ApplicationUser>>(options =>
    36                 options.UseSqlServer(Configuration.GetConnectionString(connectionString)));
     31                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    3732
    3833            services.AddIdentityServerAndIdentityProvider(AppSettings);
  • src/FinkiChattery/FinkiChattery.Identity/appsettings.Development.json

    r79ae621 r81e1ed6  
    1010                                {
    1111                                        "allowedCorsOrigins": [ "http://localhost:4200" ],
    12                                         "redirectUris": [ "http://localhost:4200/auth-callback" ],
     12                                        "redirectUris": [ "http://localhost:4200" ],
    1313                                        "postLogoutRedirectUris": [ "http://localhost:4200" ]
    1414                                }
     
    1717        },
    1818        "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;"
    2120        }
    2221}
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerConfig.cs

    r79ae621 r81e1ed6  
    2222            builder.Property(x => x.CorrectAnswer).HasColumnName(@"CorrectAnswer").HasColumnType("bit").IsRequired();
    2323            builder.Property(x => x.CreatedOn).HasColumnName(@"CreatedOn").HasColumnType("smalldatetime").IsRequired();
    24             builder.Property(x => x.UpvotesCount).HasColumnName(@"UpvotesCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);
    2524
    2625            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  
    2828
    2929            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);
    3131        }
    3232    }
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/QuestionCategoryConfig.cs

    r79ae621 r81e1ed6  
    2626
    2727            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);
    2929        }
    3030    }
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/QuestionConfig.cs

    r79ae621 r81e1ed6  
    2424            builder.Property(x => x.Views).HasColumnName(@"Views").HasColumnType("bigint").IsRequired().HasDefaultValue(0);
    2525            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);
    2826
    2927            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  
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Answer.cs

    r79ae621 r81e1ed6  
    2020        public DateTime CreatedOn { get; set; }
    2121
    22         public long UpvotesCount { get; set; }
    23 
    2422        public virtual ICollection<Upvote> Upvotes { get; set; }
    2523
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Question.cs

    r79ae621 r81e1ed6  
    3232        public DateTime LastActiveOn { get; set; }
    3333
    34         public string Search { get; set; }
    35 
    36         public long AnswersCount { get; set; }
    37 
    3834        public virtual ICollection<Answer> Answers { get; set; }
    3935
    4036        public virtual ICollection<QuestionCategory> QuestionCategories { get; set; }
     37
     38        // TODO: Pole po koe ke pravime queries
    4139    }
    4240}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/Repository.cs

    r79ae621 r81e1ed6  
    88namespace FinkiChattery.Persistence.Repositories
    99{
    10     public abstract class Repository<T> : IRepository<T> where T : BaseEntity
     10    public abstract class Repository<T> where T : BaseEntity
    1111    {
    1212        public Repository(ApplicationDbContext dbContext)
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/ICategoriesRepo.cs

    r79ae621 r81e1ed6  
    88namespace FinkiChattery.Persistence.Repositories
    99{
    10     public interface ICategoriesRepo : IRepository<Category>
     10    public interface ICategoriesRepo
    1111    {
    1212        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
     1namespace FinkiChattery.Persistence.Repositories
    82{
    9     public interface IQuestionRepo : IRepository<Question>
     3    public interface IQuestionRepo
    104    {
    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();
    185    }
    196}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs

    r79ae621 r81e1ed6  
    44namespace FinkiChattery.Persistence.Repositories
    55{
    6     public interface IStudentRepo : IRepository<Student>
     6    public interface IStudentRepo
    77    {
    88        public Task<Student> GetStudent(long applicationUserFk);
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/ITeamRepo.cs

    r79ae621 r81e1ed6  
    1 using FinkiChattery.Persistence.Models;
    2 using System;
     1using System;
    32using System.Threading.Tasks;
    43
    54namespace FinkiChattery.Persistence.Repositories
    65{
    7     public interface ITeamRepo : IRepository<Team>
     6    public interface ITeamRepo
    87    {
    98        public Task<bool> TeamWithUidExists(Guid teamUid);
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs

    r79ae621 r81e1ed6  
    11using FinkiChattery.Persistence.Context;
    22using 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;
    123
    134namespace FinkiChattery.Persistence.Repositories
     
    189        {
    1910        }
    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 DbSet
    36                 .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 DbSet
    54               .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 rawQuery
    137                 .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         }
    14611    }
    14712}
  • src/FinkiChattery/FinkiChattery.Queries/FinkiChattery.Queries.csproj

    r79ae621 r81e1ed6  
    1 <Project Sdk="Microsoft.NET.Sdk">
     1<Project Sdk="Microsoft.NET.Sdk">
    22
    33  <PropertyGroup>
    44    <TargetFramework>netcoreapp3.1</TargetFramework>
    5     <Configurations>Debug;Release;Debug_Docker</Configurations>
    65  </PropertyGroup>
    76
    8   <ItemGroup>
    9     <ProjectReference Include="..\FinkiChattery.Common\FinkiChattery.Common.csproj" />
    10     <ProjectReference Include="..\FinkiChattery.Persistence\FinkiChattery.Persistence.csproj" />
    11   </ItemGroup>
    12 
    137</Project>
  • src/FinkiChattery/FinkiChattery.sln

    r79ae621 r81e1ed6  
    2222Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "FinkiChattery.HangfireDatabase", "FinkiChattery.HangfireDatabase\FinkiChattery.HangfireDatabase.sqlproj", "{C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}"
    2323EndProject
    24 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FinkiChattery.Commands", "FinkiChattery.Commands\FinkiChattery.Commands.csproj", "{7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}"
     24Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinkiChattery.Commands", "FinkiChattery.Commands\FinkiChattery.Commands.csproj", "{7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}"
    2525EndProject
    26 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FinkiChattery.Queries", "FinkiChattery.Queries\FinkiChattery.Queries.csproj", "{D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}"
     26Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinkiChattery.Queries", "FinkiChattery.Queries\FinkiChattery.Queries.csproj", "{D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}"
    2727EndProject
    2828Global
    2929        GlobalSection(SolutionConfigurationPlatforms) = preSolution
    30                 Debug_Docker|Any CPU = Debug_Docker|Any CPU
    3130                Debug|Any CPU = Debug|Any CPU
    3231                Release|Any CPU = Release|Any CPU
    3332        EndGlobalSection
    3433        GlobalSection(ProjectConfigurationPlatforms) = postSolution
    35                 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug_Docker|Any CPU.ActiveCfg = Debug_Docker|Any CPU
    36                 {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    3734                {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    3835                {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Debug|Any CPU.Build.0 = Debug|Any CPU
    3936                {8CD7796E-5DC1-413D-8D04-3C95E89BB214}.Release|Any CPU.ActiveCfg = Release|Any CPU
    4037                {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 CPU
    42                 {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    4338                {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    4439                {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    4540                {D2EADD50-983E-48EE-8B36-7CF088DCF8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    4641                {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 CPU
    48                 {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    4942                {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    5043                {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Debug|Any CPU.Build.0 = Debug|Any CPU
    5144                {1BA385FA-32FC-4237-85D2-705EEBCAFD06}.Release|Any CPU.ActiveCfg = Release|Any CPU
    5245                {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 CPU
    54                 {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    5546                {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    5647                {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    5748                {4D2F08BF-44F0-427D-B5B8-079233500FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    5849                {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 CPU
    60                 {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    6150                {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    6251                {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
    6352                {E69CDEE7-B2F5-42F4-81F0-11DCDC1C8CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
    6453                {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 CPU
    66                 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    67                 {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug_Docker|Any CPU.Deploy.0 = Debug_Docker|Any CPU
    6854                {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    6955                {5E1219F5-FC7D-4138-811D-26934E946D30}.Debug|Any CPU.Build.0 = Debug|Any CPU
     
    7258                {5E1219F5-FC7D-4138-811D-26934E946D30}.Release|Any CPU.Build.0 = Release|Any CPU
    7359                {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 CPU
    75                 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    76                 {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug_Docker|Any CPU.Deploy.0 = Debug_Docker|Any CPU
    7760                {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    7861                {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Debug|Any CPU.Build.0 = Debug|Any CPU
     
    8164                {C4CB8596-3F3A-4B4E-BEC5-0720FF7CD532}.Release|Any CPU.Build.0 = Release|Any CPU
    8265                {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 CPU
    84                 {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    8566                {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    8667                {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
    8768                {7AD9C86F-46FF-442A-B134-3CE2E82CE0D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
    8869                {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 CPU
    90                 {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug_Docker|Any CPU.Build.0 = Debug_Docker|Any CPU
    9170                {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    9271                {D19820A3-E6E6-4B1C-927A-C8B8B4B16D25}.Debug|Any CPU.Build.0 = Debug|Any CPU
Note: See TracChangeset for help on using the changeset viewer.