Changeset f3c4950


Ignore:
Timestamp:
01/19/22 16:59:58 (2 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
6738cc0
Parents:
fcc3080
Message:

Moderator can create categories

Location:
src
Files:
22 added
20 edited
1 moved

Legend:

Unmodified
Added
Removed
  • src/Clients/Angular/finki-chattery/src/app/app-routing.module.ts

    rfcc3080 rf3c4950  
    22import { Routes, RouterModule } from '@angular/router';
    33import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
     4import { AuthorizedModeratorGuard } from './core/guards/authorized-moderator.guard';
    45import { AuthorizedGuard } from './core/guards/authorized.guard';
    56
     
    910    canActivate: [AuthorizedGuard],
    1011    loadChildren: () => import('./modules/questioning/questioning.module').then((x) => x.QuestioningModule)
     12  },
     13  {
     14    path: 'moderating',
     15    canActivate: [AuthorizedGuard, AuthorizedModeratorGuard],
     16    loadChildren: () => import('./modules/moderating/moderating.module').then((x) => x.ModeratingModule)
    1117  },
    1218  {
  • src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts

    rfcc3080 rf3c4950  
    6464  }
    6565
     66  public isModerator(): boolean {
     67    return this.user !== null && this.user.userType === ApplicationUserType.Moderator;
     68  }
     69
    6670  public currentUser(): ApplicationUser | null {
    6771    return this.user;
  • src/Clients/Angular/finki-chattery/src/app/core/services/redirect.service.ts

    rfcc3080 rf3c4950  
    2121        case ApplicationUserType.Teacher:
    2222          break;
     23        case ApplicationUserType.Moderator:
     24          this.router.navigateByUrl(`moderating/categories`);
     25          break;
    2326      }
    2427    }
  • src/Clients/Angular/finki-chattery/src/app/core/state/category-facade.service.ts

    rfcc3080 rf3c4950  
    66
    77import { CategoryStateViewModel } from 'src/app/shared-app/models';
    8 import { EffectStartedWorking, GetCategoriesState } from './category-state/category.actions';
     8import { AddCategory, EffectStartedWorking, GetCategoriesState } from './category-state/category.actions';
    99import { categoriesStateQuery } from './category-state/category.selectors';
    1010import { CategoryState } from './category-state/category.state';
     
    1717
    1818  constructor(private store: Store<CategoryState>) {
    19     this.effectWorking$ = this.store.select(categoriesStateQuery.effectWorking).pipe(
    20       filter((effect) => effect !== null),
    21       map((effect) => {
    22         if (effect instanceof HttpErrorResponse) {
    23           throw effect;
    24         } else {
    25           return effect;
    26         }
    27       }),
    28       catchError((err) => {
    29         return throwError(err);
    30       })
    31     );
     19    this.effectWorking$ = this.store.select(categoriesStateQuery.effectWorking).pipe(filter((effect) => effect !== null));
    3220  }
    3321
     
    3826  public fetchCategories(): void {
    3927    this.dispatchEffect(new GetCategoriesState());
     28  }
     29
     30  public addNewCategory(name: string): void {
     31    this.dispatchEffect(new AddCategory(name));
    4032  }
    4133
  • src/Clients/Angular/finki-chattery/src/app/core/state/category-state/category.actions.ts

    rfcc3080 rf3c4950  
    22import { Action } from '@ngrx/store';
    33import { CategoryStateViewModel } from 'src/app/shared-app/models';
    4 import { CategoryStateResponse } from './category-state.models';
    54
    65export enum CategoryActionTypes {
    76  GetCategoriesState = '[Category] Get state',
    87  GetCategoriesStateSuccess = '[Category] Get state success',
     8  AddCategory = '[Category] AddCategory',
     9  AddCategorySuccess = '[Category] AddCategorySuccess success',
    910  EffectStartedWorking = '[Category] Effect Started Working',
    1011  EffectFinishedWorking = '[Category] Effect Finished Working',
     
    2223
    2324  constructor(public payload: CategoryStateViewModel[]) {}
     25}
     26
     27export class AddCategory implements Action {
     28  readonly type = CategoryActionTypes.AddCategory;
     29
     30  constructor(public name: string) {}
     31}
     32
     33export class AddCategorySuccess implements Action {
     34  readonly type = CategoryActionTypes.AddCategorySuccess;
     35
     36  constructor(public payload: CategoryStateViewModel) {}
    2437}
    2538
     
    4255}
    4356
    44 export type CategoryAction = GetCategoriesStateSuccess | EffectStartedWorking | EffectFinishedWorking | EffectFinishedWorkingError;
     57export type CategoryAction =
     58  | GetCategoriesStateSuccess
     59  | AddCategorySuccess
     60  | EffectStartedWorking
     61  | EffectFinishedWorking
     62  | EffectFinishedWorkingError;
  • src/Clients/Angular/finki-chattery/src/app/core/state/category-state/category.effects.ts

    rfcc3080 rf3c4950  
    11import { Injectable } from '@angular/core';
    22import { Actions, createEffect, ofType } from '@ngrx/effects';
    3 import { catchError, switchMap } from 'rxjs/operators';
     3import { catchError, mergeMap, switchMap } from 'rxjs/operators';
    44import { TranslateFromJsonService } from 'src/app/shared-app/services';
    55
    66import { BaseApiService } from 'src/app/shared-app/services/base-api.service';
     7import { CreateCategoryRequest } from './category-state-request.models';
    78import { CategoryStateResponse } from './category-state.models';
    89import {
     10  AddCategory,
     11  AddCategorySuccess,
    912  CategoryActionTypes,
    1013  EffectFinishedWorking,
     
    3538    );
    3639  });
     40
     41  addCategory$ = createEffect(() => {
     42    return this.actions$.pipe(
     43      ofType<AddCategory>(CategoryActionTypes.AddCategory),
     44      mergeMap((action) => {
     45        return this.api.post<string>(`v1/categories`, new CreateCategoryRequest(action.name)).pipe(
     46          switchMap((state) => [
     47            new AddCategorySuccess(CategoriesMapper.ToCategoryStateViewModel(state, action.name, this.translate)),
     48            new EffectFinishedWorking()
     49          ]),
     50          catchError((err) => [new EffectFinishedWorkingError(err)])
     51        );
     52      })
     53    );
     54  });
    3755}
  • src/Clients/Angular/finki-chattery/src/app/core/state/category-state/category.mapper.ts

    rfcc3080 rf3c4950  
    1010    return categoriesResponse.map((x) => new CategoryStateViewModel(x.uid, x.name, translate.instant(x.name)));
    1111  }
     12
     13  public static ToCategoryStateViewModel(uid: string, name: string, translate: TranslateFromJsonService): CategoryStateViewModel {
     14    return new CategoryStateViewModel(uid, name, translate.instant(name));
     15  }
    1216}
  • src/Clients/Angular/finki-chattery/src/app/core/state/category-state/category.reducers.ts

    rfcc3080 rf3c4950  
    99        categories: action.payload
    1010      };
     11    case CategoryActionTypes.AddCategorySuccess: {
     12      return {
     13        ...state,
     14        categories: [action.payload, ...state.categories]
     15      };
     16    }
    1117    case CategoryActionTypes.EffectStartedWorking: {
    1218      return {
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/header/header.component.html

    rfcc3080 rf3c4950  
    1616      'header-ask-question' | translate
    1717    }}</app-button>
    18     <app-button [matMenuTriggerFor]="menu" class="margin-y-xs" *ngIf="auth.isLoggedIn()" [buttonType]="ButtonType.Basic">{{
     18    <app-button [matMenuTriggerFor]="menu" class="margin-y-xs" *ngIf="auth.isStudent()" [buttonType]="ButtonType.Basic">{{
    1919      'header-student-questions' | translate
    2020    }}</app-button>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/header/header.component.ts

    rfcc3080 rf3c4950  
    2828      this.router.navigateByUrl('questioning/preview');
    2929    }
     30    if (this.auth.isModerator()) {
     31      this.router.navigateByUrl('moderating/categories');
     32    }
    3033  }
    3134
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    rfcc3080 rf3c4950  
    8181  "AnswerAlreadyUpvoted": "You have already upvoted this answer",
    8282  "AnswerAlreadyDownvoted": "You have already downvoted this answer",
    83   "StudentHasBadReputation": "You have bad reputation and can not vote"
     83  "StudentHasBadReputation": "You have bad reputation and can not vote",
     84  "CategoryNameAlreadyExists": "Category with that name already exists",
     85  "category-title": "Category title",
     86  "create-new-category": "Create new category"
    8487}
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Authentication/AuthenticationPolicy.cs

    rfcc3080 rf3c4950  
    44    {
    55        public const string Student = "Student";
     6        public const string Moderator = "Moderator";
    67    }
    78}
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/CategoriesController.cs

    rfcc3080 rf3c4950  
    1 using FinkiChattery.Api.ApplicationServices.Questioning;
     1using FinkiChattery.Api.ApplicationServices.Authentication;
     2using FinkiChattery.Api.ApplicationServices.Questioning;
     3using FinkiChattery.Commands.Moderating;
    24using FinkiChattery.Common.Mediator.Interfaces;
     5using FinkiChattery.Contracts.Moderating;
    36using FinkiChattery.Queries.Questioning;
     7using IdentityServer4.AccessTokenValidation;
    48using Microsoft.AspNetCore.Authorization;
    59using Microsoft.AspNetCore.Mvc;
     
    2731            return Ok(categoriesList.ToCategoryDtos());
    2832        }
     33
     34        [HttpPost]
     35        [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Moderator)]
     36        public async Task<IActionResult> CreateNewCategory([FromBody] CreateCategoryRequest request)
     37        {
     38            var categoryUid = await MediatorService.SendAsync(new CreateCategoryCommand(request.Name));
     39            return Ok(categoryUid);
     40        }
    2941    }
    3042}
  • src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs

    rfcc3080 rf3c4950  
    9494            {
    9595                options.AddPolicy(AuthenticationPolicy.Student, policy => policy.Requirements.Add(new StudentRequirement()));
     96                options.AddPolicy(AuthenticationPolicy.Moderator, policy => policy.Requirements.Add(new ModeratorRequirement()));
    9697
    9798            });
  • src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj

    rfcc3080 rf3c4950  
    7676    <Folder Include="Snapshots" />
    7777    <Folder Include="dbo\Tables\Vote" />
     78    <Folder Include="dbo\Tables\Moderator" />
    7879  </ItemGroup>
    7980  <ItemGroup>
    80     <Build Include="dbo\Tables\Moderator.sql" />
    8181    <Build Include="dbo\Tables\Teacher.sql" />
    8282    <Build Include="dbo\Tables\StudentTeam.sql" />
     
    101101    <None Include="dbo\Tables\QuestionCategory\QuestionCategory.Debug.Seed.sql" />
    102102    <Build Include="dbo\Tables\Vote\Vote.sql" />
     103    <Build Include="dbo\Tables\Moderator\Moderator.sql" />
     104    <None Include="dbo\Tables\Moderator\Moderator.Debug.Seed.sql" />
    103105  </ItemGroup>
    104106  <ItemGroup>
  • src/FinkiChattery/FinkiChattery.Database/dbo/Scripts/PostDeploymentScripts/Debug.PostDeployment.sql

    rfcc3080 rf3c4950  
    66:r ./../../Tables/AnswerResponse/AnswerResponse.Debug.Seed.sql
    77:r ./../../Tables/QuestionCategory/QuestionCategory.Debug.Seed.sql
     8:r ./../../Tables/Moderator/Moderator.Debug.Seed.sql
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/User/Seed/Users.Debug.Seed.sql

    rfcc3080 rf3c4950  
    77        FROM (
    88        VALUES
    9                 (1, N'stojkovmarko1@gmail.com',  N'STOJKOVMARKO1@GMAIL.COM', 'stojkovmarko1@gmail.com',  N'STOJKOVMARKO1@GMAIL.COM', 1, N'AQAAAAEAACcQAAAAELBRjOGmA/Dh8BPvcPMA1vmk1jjvyDd/9/lO/3shoBt7z9NfiKJR+Ckp/2+6BVpyLw==', N'MMDEZ3RWADCRM4PTTKSEZNQYNH5V7UVA', N'08309369-24a1-470c-9ab1-65a250cafd4e', null, 0, 0, null, 1, 0, 0)
     9                (1, N'stojkovmarko1@gmail.com',  N'STOJKOVMARKO1@GMAIL.COM', 'stojkovmarko1@gmail.com',  N'STOJKOVMARKO1@GMAIL.COM', 1, N'AQAAAAEAACcQAAAAELBRjOGmA/Dh8BPvcPMA1vmk1jjvyDd/9/lO/3shoBt7z9NfiKJR+Ckp/2+6BVpyLw==', N'MMDEZ3RWADCRM4PTTKSEZNQYNH5V7UVA', N'08309369-24a1-470c-9ab1-65a250cafd4e', null, 0, 0, null, 1, 0, 0),
     10                (2, N'stojkovmarko1234@gmail.com',  N'STOJKOVMARKO1234@GMAIL.COM', 'stojkovmarko1234@gmail.com',  N'STOJKOVMARKO1234@GMAIL.COM', 1, N'AQAAAAEAACcQAAAAELBRjOGmA/Dh8BPvcPMA1vmk1jjvyDd/9/lO/3shoBt7z9NfiKJR+Ckp/2+6BVpyLw==', N'MMDEZ3RWADCRM4PTTKSEZNQYNH5V7UVA', N'08309369-24a1-470c-9ab1-65a250cafd4e', null, 0, 0, null, 1, 0, 2)
    1011                )
    1112                AS temp ([ID], [UserName], [NormalizedUserName], [Email], [NormalizedEmail], [EmailConfirmed], [PasswordHash], [SecurityStamp], [ConcurrencyStamp], [PhoneNumber], [PhoneNumberConfirmed], [TwoFactorEnabled], [LockoutEnd], [LockoutEnabled], [AccessFailedCount], [Role])
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Category.cs

    rfcc3080 rf3c4950  
    1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Threading.Tasks;
    6 
    7 namespace FinkiChattery.Persistence.Models
     1namespace FinkiChattery.Persistence.Models
    82{
    93    public class Category : BaseEntity
    104    {
     5        public Category() : base()
     6        {
     7        }
     8
    119        public string Name { get; set; }
    1210    }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/ICategoriesRepo.cs

    rfcc3080 rf3c4950  
    1212        public Task<bool> CategoriesExist(IEnumerable<Guid> categoriesUids);
    1313
     14        public Task<bool> CategoryWithNameIsUnique(string name);
     15
    1416        public Task<IEnumerable<Category>> GetCategories(IEnumerable<Guid> categoriesUids);
    1517    }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/CategoriesRepo.cs

    rfcc3080 rf3c4950  
    2020        }
    2121
     22        public async Task<bool> CategoryWithNameIsUnique(string name)
     23        {
     24            return !(await DbSet.AnyAsync(x => x.Name == name));
     25        }
     26
    2227        public async Task<IEnumerable<Category>> GetCategories(IEnumerable<Guid> categoriesUids)
    2328        {
Note: See TracChangeset for help on using the changeset viewer.