diff options
54 files changed, 1408 insertions, 305 deletions
diff --git a/src/DevHive.Angular/src/app/app-constants.module.ts b/src/DevHive.Angular/src/app/app-constants.module.ts index 7552a5e..cbb1ec1 100644 --- a/src/DevHive.Angular/src/app/app-constants.module.ts +++ b/src/DevHive.Angular/src/app/app-constants.module.ts @@ -8,5 +8,9 @@ export class AppConstants { public static API_LANGUAGE_URL = AppConstants.BASE_API_URL + '/Language'; public static API_TECHNOLOGY_URL = AppConstants.BASE_API_URL + '/Technology'; + public static API_POST_URL = AppConstants.BASE_API_URL + '/Post'; + public static API_FEED_URL = AppConstants.BASE_API_URL + '/Feed'; + + public static PAGE_SIZE = 5; public static FALLBACK_PROFILE_ICON = 'assets/images/feed/profile-pic.png'; } diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.css b/src/DevHive.Angular/src/app/components/feed/feed.component.css index e22693e..f810e83 100644 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.css +++ b/src/DevHive.Angular/src/app/components/feed/feed.component.css @@ -109,6 +109,15 @@ display: none; } +/* Posts */ + +#no-posts-msg { + width: 100%; + margin-top: 1em; + color: gray; + text-align: center; +} + /* Elements, that act as buttons */ #profile-bar > #profile-info:hover, diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.html b/src/DevHive.Angular/src/app/components/feed/feed.component.html index b82b189..e3c6e83 100644 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.html +++ b/src/DevHive.Angular/src/app/components/feed/feed.component.html @@ -23,8 +23,12 @@ </a> </nav> <div id="posts" class="scroll-standalone"> - <div *ngFor="let post of posts" class="post"> - <app-post></app-post> + <div id="no-posts-msg" *ngIf="posts.length === 0"> + None of your friends have posted anything yet!<br> + Try refreshing your page! + </div> + <div *ngFor="let friendPost of posts" class="post"> + <app-post [paramId]="friendPost.postId.toString()"></app-post> </div> </div> </div> diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.ts b/src/DevHive.Angular/src/app/components/feed/feed.component.ts index b027e5b..f260fd4 100644 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.ts +++ b/src/DevHive.Angular/src/app/components/feed/feed.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { User } from 'src/models/identity/user'; -import { PostComponent } from '../post/post.component'; import { UserService } from '../../services/user.service'; import { AppConstants } from 'src/app/app-constants.module'; import {HttpErrorResponse} from '@angular/common/http'; +import {FeedService} from 'src/app/services/feed.service'; +import {Post} from 'src/models/post'; @Component({ selector: 'app-feed', @@ -14,26 +15,24 @@ import {HttpErrorResponse} from '@angular/common/http'; }) export class FeedComponent implements OnInit { private _title = 'Feed'; + private _timeLoaded: string; public dataArrived = false; public user: User; - public posts: PostComponent[]; + public posts: Post[] = []; - constructor(private _titleService: Title, private _router: Router, private _userService: UserService) { + constructor(private _titleService: Title, private _router: Router, private _userService: UserService, private _feedService: FeedService) { this._titleService.setTitle(this._title); } ngOnInit(): void { this.user = this._userService.getDefaultUser(); - this.posts = [ - new PostComponent(), - new PostComponent(), - new PostComponent(), - new PostComponent(), - ]; + const now = new Date(); + now.setHours(now.getHours() + 2); // accounting for eastern european timezone + this._timeLoaded = now.toISOString(); if (sessionStorage.getItem('UserCred')) { this._userService.getUserFromSessionStorageRequest().subscribe( - (res: object) => this.finishUserLoading(res), + (res: object) => this.loadFeed(res), (err: HttpErrorResponse) => this.bailOnBadToken() ); } else { @@ -41,8 +40,21 @@ export class FeedComponent implements OnInit { } } - private finishUserLoading(res: object): void { + private loadFeed(res: object): void { Object.assign(this.user, res); + + this._feedService.getUserFeedFromSessionStorageRequest(1, this._timeLoaded, AppConstants.PAGE_SIZE).subscribe( + (result: object) => { + this.posts = Object.values(result)[0]; + this.finishUserLoading(); + }, + (err: HttpErrorResponse) => { + this.finishUserLoading(); + } + ); + } + + private finishUserLoading(): void { if (this.user.imageUrl === '') { this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; } diff --git a/src/DevHive.Angular/src/app/components/post/post.component.css b/src/DevHive.Angular/src/app/components/post/post.component.css index 5395eb2..c3e3caa 100644 --- a/src/DevHive.Angular/src/app/components/post/post.component.css +++ b/src/DevHive.Angular/src/app/components/post/post.component.css @@ -24,6 +24,10 @@ hr { margin-bottom: .2em; } +.author:hover { + cursor: pointer; +} + .author > img { width: 2.2em; height: 2.2em; diff --git a/src/DevHive.Angular/src/app/components/post/post.component.html b/src/DevHive.Angular/src/app/components/post/post.component.html index 487b785..8fbda35 100644 --- a/src/DevHive.Angular/src/app/components/post/post.component.html +++ b/src/DevHive.Angular/src/app/components/post/post.component.html @@ -1,6 +1,6 @@ -<div class="post rounded-border"> +<div class="post rounded-border" *ngIf="loaded"> <div class="content"> - <div class="author"> + <div class="author" (click)="goToAuthorProfile()"> <img class="round-image" [src]="user.imageUrl"> <div class="author-info"> <div class="name"> @@ -12,10 +12,10 @@ </div> </div> <div class="message"> - Your opinion on my idea? + {{ post.message }} </div> <div class="timestamp"> - 17:41 - 27 Dec 2020 + {{ timeCreated }} </div> </div> <div class="rating"> diff --git a/src/DevHive.Angular/src/app/components/post/post.component.ts b/src/DevHive.Angular/src/app/components/post/post.component.ts index 76a4873..7a6b5c0 100644 --- a/src/DevHive.Angular/src/app/components/post/post.component.ts +++ b/src/DevHive.Angular/src/app/components/post/post.component.ts @@ -1,7 +1,12 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import {Router} from '@angular/router'; import { Guid } from 'guid-typescript'; import {AppConstants} from 'src/app/app-constants.module'; +import {FeedService} from 'src/app/services/feed.service'; +import {PostService} from 'src/app/services/post.service'; +import {UserService} from 'src/app/services/user.service'; import { User } from 'src/models/identity/user'; +import {Post} from 'src/models/post'; @Component({ selector: 'app-post', @@ -10,23 +15,39 @@ import { User } from 'src/models/identity/user'; }) export class PostComponent implements OnInit { public user: User; + public post: Post; public votesNumber: number; + public timeCreated: string; + public loaded = false; + @Input() paramId: string; - constructor() {} + constructor(private _postService: PostService, private _userService: UserService, private _router: Router) + {} ngOnInit(): void { - // Fetch data in post service - this.user = new User( - Guid.create(), - 'gosho_trapov', - 'Gosho', - 'Trapov', - 'gotra@bg.com', - AppConstants.FALLBACK_PROFILE_ICON, - new Array(), - new Array() - ); + this.post = this._postService.getDefaultPost(); + this.user = this._userService.getDefaultUser(); + this._postService.getPostRequest(Guid.parse(this.paramId)).subscribe( + (result: object) => { + Object.assign(this.post, result); + this.timeCreated = new Date(this.post.timeCreated).toLocaleString('en-GB'); + this.loadUser(); + } + ); this.votesNumber = 23; } + + private loadUser(): void { + this._userService.getUserByUsernameRequest(this.post.creatorUsername).subscribe( + (result: object) => { + Object.assign(this.user, result); + this.loaded = true; + } + ); + } + + goToAuthorProfile(): void { + this._router.navigate(['/profile/' + this.user.userName]); + } } diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css index 7e0978d..2aecef5 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css @@ -50,12 +50,40 @@ hr { transform: translate(0, -1.2em); } +#all-languages, #all-technologies { + display: flex; + flex-wrap: wrap; +} + +#all-languages > *, #all-technologies > * { + width: fit-content; + margin-top: .1em; + margin-bottom: .1em; +} + /* Buttons */ +.edit-btn { + border-radius: 0 !important; + color: var(--focus-color); + background-color: white; + border-color: var(--focus-color); +} + +.edit-btn:hover { + color: white; + background-color: black; + border-color: black !important; +} + .submit-btn { margin-bottom: .5em; } +#update-profile-btn { + margin-top: 1em; +} + #delete-account:hover { color: indianred; border-color: indianred !important; diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html index 16b537c..1b713d7 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html @@ -58,28 +58,42 @@ <label *ngIf="updateUserFormGroup.get('password')?.errors?.pattern" class="error">*At least 1 number</label> </div> </div> - <button type="button" (click)="toggleLanguages()">Edit Languages</button> + <button type="button" class="submit-btn edit-btn" (click)="toggleLanguages()">▼ Edit Languages ▼</button> <div *ngIf="showLanguages"> - Type in your desired languages, separated by a space. - <input type="text" class="input-field" formControlName="languageInput" required> + <div class="input-selection"> + <input type="text" class="input-field" formControlName="languageInput" required> + + <div class="input-errors"> + <label class="error">Type in your desired languages, separated by a space</label> + </div> + </div> Available languages: - <div *ngFor="let lang of availableLanguages"> - {{ lang.name }} + <div id="all-languages"> + <div class="user-language" *ngFor="let lang of availableLanguages"> + {{ lang.name }} + </div> </div> </div> - <button type="button" (click)="toggleTechnologies()">Edit Technologies</button> + <button type="button" class="submit-btn edit-btn" (click)="toggleTechnologies()">▼ Edit Technologies ▼</button> <div *ngIf="showTechnologies"> - Type in your desired technologies, separated by a space. - <input type="text" class="input-field" formControlName="technologyInput" required> + <div class="input-selection"> + <input type="text" class="input-field" formControlName="technologyInput" required> + + <div class="input-errors"> + <label class="error">Type in your desired technologies, separated by a space</label> + </div> + </div> Available technologies: - <div *ngFor="let tech of availableTechnologies"> - {{ tech.name }} + <div id="all-technologies"> + <div class="user-technology" *ngFor="let tech of availableTechnologies"> + {{ tech.name }} + </div> </div> </div> - <button class="submit-btn" type="submit">Update profile</button> + <button id="update-profile-btn" class="submit-btn" type="submit">Update profile</button> <app-success-bar></app-success-bar> <app-error-bar></app-error-bar> </form> diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts index 5be160e..e348b8b 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts @@ -126,8 +126,8 @@ export class ProfileSettingsComponent implements OnInit { .then(value => this.updateUserFormGroup.patchValue({ technologyInput : value })); this.updateUserFormGroup.valueChanges.subscribe(() => { - this._successBar.hideMsg(); - this._errorBar.hideError(); + this._successBar?.hideMsg(); + this._errorBar?.hideError(); }); } @@ -185,7 +185,9 @@ export class ProfileSettingsComponent implements OnInit { // Transfer user input to objects of type { "name": "value" } const actualLanguages = []; for (const lName of names) { - actualLanguages.push({ name : lName }); + if (lName !== '') { + actualLanguages.push({ name : lName }); + } } // Add the data to the form (to the value that is going to be sent) @@ -211,7 +213,9 @@ export class ProfileSettingsComponent implements OnInit { // Transfer user input to objects of type { "name": "value" } const actualTechnologies = []; for (const tName of names) { - actualTechnologies.push({ name : tName }); + if (tName !== '') { + actualTechnologies.push({ name : tName }); + } } // Add the data to the form (to the value that is going to be sent) diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.css b/src/DevHive.Angular/src/app/components/profile/profile.component.css index 7d96624..f312ebd 100644 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.css +++ b/src/DevHive.Angular/src/app/components/profile/profile.component.css @@ -74,16 +74,15 @@ hr { align-items: center; } -.user-language, .user-technology { - border-radius: .4em; - background-color: lightgrey; - padding: .2em; - margin: 0 .2em; -} - /* Posts */ -#posts { +#no-posts { width: 100%; + text-align: center; + color: gray; + margin-top: .2em; } +#posts { + width: 100%; +} diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.html b/src/DevHive.Angular/src/app/components/profile/profile.component.html index 43d580f..ac0c07e 100644 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.html +++ b/src/DevHive.Angular/src/app/components/profile/profile.component.html @@ -41,9 +41,14 @@ None </div> </div> + <hr> <div id="posts"> - <hr> - Posts go here + <div id="no-posts" *ngIf="userPosts.length === 0"> + {{ user.firstName }} {{ user.lastName }} hasn't posted anything yet! + </div> + <div *ngFor="let userPost of userPosts"> + <app-post [paramId]="userPost.postId.toString()"></app-post> + </div> </div> </div> </div> diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.ts b/src/DevHive.Angular/src/app/components/profile/profile.component.ts index 69d7e72..7717505 100644 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.ts +++ b/src/DevHive.Angular/src/app/components/profile/profile.component.ts @@ -7,6 +7,8 @@ import {HttpErrorResponse} from '@angular/common/http'; import {Location} from '@angular/common'; import {LanguageService} from 'src/app/services/language.service'; import {TechnologyService} from 'src/app/services/technology.service'; +import {Post} from 'src/models/post'; +import {FeedService} from 'src/app/services/feed.service'; @Component({ selector: 'app-profile', @@ -18,10 +20,11 @@ export class ProfileComponent implements OnInit { public loggedInUser = false; public dataArrived = false; public user: User; + public userPosts: Post[] = []; public showNoLangMsg = false; public showNoTechMsg = false; - constructor(private _router: Router, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _location: Location) + constructor(private _router: Router, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _feedService: FeedService, private _location: Location) { } private setDefaultUser(): void { @@ -57,19 +60,34 @@ export class ProfileComponent implements OnInit { private loadTechnologies(): void { if (this.user.technologies.length > 0) { - // When user has technologies, get their names and finally finish loading + // When user has technologies, get their names and then load posts this._technologyService.getFullTechnologiesFromIncomplete(this.user.technologies) .then(value => { this.user.technologies = value; - this.finishUserLoading(); + this.loadPosts(); }); } else { this.showNoTechMsg = true; - this.finishUserLoading(); + this.loadPosts(); } } + private loadPosts(): void { + const now = new Date(); + now.setHours(now.getHours() + 2); // accounting for eastern europe timezone + + this._feedService.getUserPostsRequest(this.user.userName, 1, now.toISOString(), AppConstants.PAGE_SIZE).subscribe( + (result: object) => { + this.userPosts = Object.values(result)[0]; + this.finishUserLoading(); + }, + (err: HttpErrorResponse) => { + this.finishUserLoading(); + } + ); + } + private finishUserLoading(): void { if (this.user.imageUrl === '') { this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; diff --git a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts b/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts index 9a12c3a..f7db0e2 100644 --- a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts +++ b/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts @@ -15,7 +15,10 @@ export class SuccessBarComponent implements OnInit { } showMsg(msg?: string | undefined): void { - if (msg === undefined || msg.trim() === '') { + if (msg === undefined) { + this.successMsg = 'Success!'; + } + else if (msg.trim() === '') { this.successMsg = 'Success!'; } else { diff --git a/src/DevHive.Angular/src/app/services/feed.service.ts b/src/DevHive.Angular/src/app/services/feed.service.ts new file mode 100644 index 0000000..cb82bcd --- /dev/null +++ b/src/DevHive.Angular/src/app/services/feed.service.ts @@ -0,0 +1,46 @@ +import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; +import {Injectable} from '@angular/core'; +import {Guid} from 'guid-typescript'; +import {Observable} from 'rxjs'; +import {IJWTPayload} from 'src/interfaces/jwt-payload'; +import {AppConstants} from '../app-constants.module'; +import jwt_decode from 'jwt-decode'; +import {IUserCredentials} from 'src/interfaces/user-credentials'; + +@Injectable({ + providedIn: 'root' +}) +export class FeedService { + constructor(private http: HttpClient) { } + + getUserFeedFromSessionStorageRequest(pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { + const jwt: IJWTPayload = { token: sessionStorage.getItem('UserCred') ?? '' }; + const userCred = jwt_decode<IUserCredentials>(jwt.token); + return this.getUserFeedRequest(userCred.ID, jwt.token, pageNumber, firstTimeIssued, pageSize); + } + + getUserFeedRequest(userId: Guid, authToken: string, pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { + const body = { + pageNumber: pageNumber, + firstPageTimeIssued: firstTimeIssued, + pageSize: pageSize + }; + const options = { + params: new HttpParams().set('UserId', userId.toString()), + headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) + }; + return this.http.post(AppConstants.API_FEED_URL + '/GetPosts', body, options); + } + + getUserPostsRequest(userName: string, pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { + const body = { + pageNumber: pageNumber, + firstPageTimeIssued: firstTimeIssued, + pageSize: pageSize + }; + const options = { + params: new HttpParams().set('UserName', userName) + }; + return this.http.post(AppConstants.API_FEED_URL + '/GetUserPosts', body, options); + } +} diff --git a/src/DevHive.Angular/src/app/services/post.service.ts b/src/DevHive.Angular/src/app/services/post.service.ts new file mode 100644 index 0000000..4cd96bc --- /dev/null +++ b/src/DevHive.Angular/src/app/services/post.service.ts @@ -0,0 +1,25 @@ +import {HttpClient, HttpParams} from '@angular/common/http'; +import {Injectable} from '@angular/core'; +import {Guid} from 'guid-typescript'; +import {Observable} from 'rxjs'; +import {Post} from 'src/models/post'; +import {AppConstants} from '../app-constants.module'; + +@Injectable({ + providedIn: 'root' +}) +export class PostService { + constructor(private http: HttpClient) { } + + getDefaultPost(): Post { + return new Post(Guid.createEmpty(), 'Gosho', 'Trapov', 'gosho_trapov', 'Your opinion on my idea?', new Date()); + } + + getPostRequest(id: Guid): Observable<object> { + const options = { + params: new HttpParams().set('Id', id.toString()) + }; + return this.http.get(AppConstants.API_POST_URL, options); + } +} + diff --git a/src/DevHive.Angular/src/models/post.ts b/src/DevHive.Angular/src/models/post.ts new file mode 100644 index 0000000..2d7d79f --- /dev/null +++ b/src/DevHive.Angular/src/models/post.ts @@ -0,0 +1,63 @@ +import {Guid} from 'guid-typescript'; + +export class Post { + private _postId: Guid; + private _creatorFirstName: string; + private _creatorLastName: string; + private _creatorUsername: string; + private _message: string; + private _timeCreated: Date; + // _comments + // _files + + constructor(postId: Guid, creatorFirstName: string, creatorLastName: string, creatorUsername: string, message: string, timeCreated: Date) { + this.postId = postId; + this.creatorFirstName = creatorFirstName; + this.creatorLastName = creatorLastName; + this.creatorUsername = creatorUsername; + this.message = message; + this.timeCreated = timeCreated; + } + + public get postId(): Guid { + return this._postId; + } + public set postId(v: Guid) { + this._postId = v; + } + + public get creatorFirstName(): string { + return this._creatorFirstName; + } + public set creatorFirstName(v: string) { + this._creatorFirstName = v; + } + + public get creatorLastName(): string { + return this._creatorLastName; + } + public set creatorLastName(v: string) { + this._creatorLastName = v; + } + + public get creatorUsername(): string { + return this._creatorUsername; + } + public set creatorUsername(v: string) { + this._creatorUsername = v; + } + + public get message(): string { + return this._message; + } + public set message(v: string) { + this._message = v; + } + + public get timeCreated(): Date { + return this._timeCreated; + } + public set timeCreated(v: Date) { + this._timeCreated = v; + } +} diff --git a/src/DevHive.Angular/src/styles.css b/src/DevHive.Angular/src/styles.css index 1320795..aaa1bd5 100644 --- a/src/DevHive.Angular/src/styles.css +++ b/src/DevHive.Angular/src/styles.css @@ -73,6 +73,13 @@ input:focus, button:focus { scrollbar-width: none; /* Firefox */ } +.user-language, .user-technology { + border-radius: .4em; + background-color: lightgrey; + padding: .26em; + margin: 0 .2em; +} + /* Inputs, the type found in login and register */ .input-selection { diff --git a/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs b/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs index e9fd48a..7262510 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs +++ b/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs @@ -8,5 +8,6 @@ namespace DevHive.Data.Interfaces.Repositories public interface IFeedRepository { Task<List<Post>> GetFriendsPosts(List<User> friendsList, DateTime firstRequestIssued, int pageNumber, int pageSize); + Task<List<Post>> GetUsersPosts(User user, DateTime firstRequestIssued, int pageNumber, int pageSize); } } diff --git a/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.Designer.cs b/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.Designer.cs new file mode 100644 index 0000000..1609c52 --- /dev/null +++ b/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.Designer.cs @@ -0,0 +1,537 @@ +// <auto-generated /> +using System; +using System.Collections.Generic; +using DevHive.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace DevHive.Data.Migrations +{ + [DbContext(typeof(DevHiveContext))] + [Migration("20210130092813_CloudinaryFileUpload")] + partial class CloudinaryFileUpload + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityByDefaultColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("DevHive.Data.Models.Comment", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<Guid?>("CreatorId") + .HasColumnType("uuid"); + + b.Property<string>("Message") + .HasColumnType("text"); + + b.Property<Guid?>("PostId") + .HasColumnType("uuid"); + + b.Property<DateTime>("TimeCreated") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("PostId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Language", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<string>("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Languages"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<Guid?>("CreatorId") + .HasColumnType("uuid"); + + b.Property<List<string>>("FileUrls") + .HasColumnType("text[]"); + + b.Property<string>("Message") + .HasColumnType("text"); + + b.Property<DateTime>("TimeCreated") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Role", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property<string>("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property<string>("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Technology", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<string>("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Technologies"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<int>("AccessFailedCount") + .HasColumnType("integer"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property<string>("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property<bool>("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property<string>("FirstName") + .HasColumnType("text"); + + b.Property<string>("LastName") + .HasColumnType("text"); + + b.Property<bool>("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property<DateTimeOffset?>("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property<string>("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property<string>("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property<string>("PasswordHash") + .HasColumnType("text"); + + b.Property<string>("PhoneNumber") + .HasColumnType("text"); + + b.Property<bool>("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property<string>("ProfilePictureUrl") + .HasColumnType("text"); + + b.Property<string>("SecurityStamp") + .HasColumnType("text"); + + b.Property<bool>("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property<Guid?>("UserId") + .HasColumnType("uuid"); + + b.Property<string>("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("UserId"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("DevHive.Data.RelationModels.UserFriends", b => + { + b.Property<Guid>("UserId") + .HasColumnType("uuid"); + + b.Property<Guid>("FriendId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "FriendId"); + + b.HasIndex("FriendId"); + + b.ToTable("UserFriends"); + }); + + modelBuilder.Entity("LanguageUser", b => + { + b.Property<Guid>("LanguagesId") + .HasColumnType("uuid"); + + b.Property<Guid>("UsersId") + .HasColumnType("uuid"); + + b.HasKey("LanguagesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("LanguageUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property<string>("ClaimType") + .HasColumnType("text"); + + b.Property<string>("ClaimValue") + .HasColumnType("text"); + + b.Property<Guid>("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property<string>("ClaimType") + .HasColumnType("text"); + + b.Property<string>("ClaimValue") + .HasColumnType("text"); + + b.Property<Guid>("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.Property<string>("LoginProvider") + .HasColumnType("text"); + + b.Property<string>("ProviderKey") + .HasColumnType("text"); + + b.Property<string>("ProviderDisplayName") + .HasColumnType("text"); + + b.Property<Guid>("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("uuid"); + + b.Property<Guid>("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("uuid"); + + b.Property<string>("LoginProvider") + .HasColumnType("text"); + + b.Property<string>("Name") + .HasColumnType("text"); + + b.Property<string>("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.Property<Guid>("RolesId") + .HasColumnType("uuid"); + + b.Property<Guid>("UsersId") + .HasColumnType("uuid"); + + b.HasKey("RolesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("RoleUser"); + }); + + modelBuilder.Entity("TechnologyUser", b => + { + b.Property<Guid>("TechnologiesId") + .HasColumnType("uuid"); + + b.Property<Guid>("UsersId") + .HasColumnType("uuid"); + + b.HasKey("TechnologiesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("TechnologyUser"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Comment", b => + { + b.HasOne("DevHive.Data.Models.User", "Creator") + .WithMany("Comments") + .HasForeignKey("CreatorId"); + + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany("Comments") + .HasForeignKey("PostId"); + + b.Navigation("Creator"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.HasOne("DevHive.Data.Models.User", "Creator") + .WithMany("Posts") + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany("Friends") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("DevHive.Data.RelationModels.UserFriends", b => + { + b.HasOne("DevHive.Data.Models.User", "Friend") + .WithMany() + .HasForeignKey("FriendId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Friend"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LanguageUser", b => + { + b.HasOne("DevHive.Data.Models.Language", null) + .WithMany() + .HasForeignKey("LanguagesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TechnologyUser", b => + { + b.HasOne("DevHive.Data.Models.Technology", null) + .WithMany() + .HasForeignKey("TechnologiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.Navigation("Comments"); + + b.Navigation("Friends"); + + b.Navigation("Posts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.cs b/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.cs new file mode 100644 index 0000000..f691c9b --- /dev/null +++ b/src/DevHive.Data/Migrations/20210130092813_CloudinaryFileUpload.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace DevHive.Data.Migrations +{ + public partial class CloudinaryFileUpload : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Comments_Posts_PostId", + table: "Comments"); + + migrationBuilder.AlterColumn<Guid>( + name: "CreatorId", + table: "Posts", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn<List<string>>( + name: "FileUrls", + table: "Posts", + type: "text[]", + nullable: true); + + migrationBuilder.AlterColumn<Guid>( + name: "PostId", + table: "Comments", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn<Guid>( + name: "CreatorId", + table: "Comments", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.CreateIndex( + name: "IX_Posts_CreatorId", + table: "Posts", + column: "CreatorId"); + + migrationBuilder.CreateIndex( + name: "IX_Comments_CreatorId", + table: "Comments", + column: "CreatorId"); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_AspNetUsers_CreatorId", + table: "Comments", + column: "CreatorId", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_Posts_PostId", + table: "Comments", + column: "PostId", + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Posts_AspNetUsers_CreatorId", + table: "Posts", + column: "CreatorId", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Comments_AspNetUsers_CreatorId", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_Comments_Posts_PostId", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_Posts_AspNetUsers_CreatorId", + table: "Posts"); + + migrationBuilder.DropIndex( + name: "IX_Posts_CreatorId", + table: "Posts"); + + migrationBuilder.DropIndex( + name: "IX_Comments_CreatorId", + table: "Comments"); + + migrationBuilder.DropColumn( + name: "FileUrls", + table: "Posts"); + + migrationBuilder.AlterColumn<Guid>( + name: "CreatorId", + table: "Posts", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn<Guid>( + name: "PostId", + table: "Comments", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn<Guid>( + name: "CreatorId", + table: "Comments", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_Posts_PostId", + table: "Comments", + column: "PostId", + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/DevHive.Data/Models/User.cs b/src/DevHive.Data/Models/User.cs index 2203554..da18c2f 100644 --- a/src/DevHive.Data/Models/User.cs +++ b/src/DevHive.Data/Models/User.cs @@ -16,15 +16,8 @@ namespace DevHive.Data.Models public string ProfilePictureUrl { get; set; } - /// <summary> - /// Languages that the user uses or is familiar with - /// </summary> - // [Unique] public HashSet<Language> Languages { get; set; } = new(); - /// <summary> - /// Technologies that the user uses or is familiar with - /// </summary> public HashSet<Technology> Technologies { get; set; } = new(); public HashSet<Role> Roles { get; set; } = new(); diff --git a/src/DevHive.Data/Repositories/CommentRepository.cs b/src/DevHive.Data/Repositories/CommentRepository.cs index d33b7bf..8ddc628 100644 --- a/src/DevHive.Data/Repositories/CommentRepository.cs +++ b/src/DevHive.Data/Repositories/CommentRepository.cs @@ -17,6 +17,14 @@ namespace DevHive.Data.Repositories } #region Read + public override async Task<Comment> GetByIdAsync(Guid id) + { + return await this._context.Comments + .Include(x => x.Creator) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Id == id); + } + public async Task<Comment> GetCommentByIssuerAndTimeCreatedAsync(Guid issuerId, DateTime timeCreated) { return await this._context.Comments @@ -25,6 +33,21 @@ namespace DevHive.Data.Repositories } #endregion + #region Update + public override async Task<bool> EditAsync(Guid id, Comment newEntity) + { + Comment comment = await this.GetByIdAsync(id); + + this._context + .Entry(comment) + .CurrentValues + .SetValues(newEntity); + + return await this.SaveChangesAsync(this._context); + } + #endregion + + #region Validations public async Task<bool> DoesCommentExist(Guid id) { diff --git a/src/DevHive.Data/Repositories/FeedRepository.cs b/src/DevHive.Data/Repositories/FeedRepository.cs index efcb8e0..d8170d0 100644 --- a/src/DevHive.Data/Repositories/FeedRepository.cs +++ b/src/DevHive.Data/Repositories/FeedRepository.cs @@ -25,11 +25,28 @@ namespace DevHive.Data.Repositories List<Post> posts = await this._context.Posts .Where(post => post.TimeCreated < firstRequestIssued) .Where(p => friendsIds.Contains(p.Creator.Id)) - .OrderByDescending(x => x.TimeCreated) .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); + // Ordering by descending can't happen in query, because it doesn't order it + // completely correctly (example: in query these two times are ordered + // like this: 2021-01-30T11:49:45, 2021-01-28T21:37:40.701244) + posts = posts.OrderByDescending(x => x.TimeCreated.ToFileTime()).ToList(); + return posts; + } + + public async Task<List<Post>> GetUsersPosts(User user, DateTime firstRequestIssued, int pageNumber, int pageSize) + { + List<Post> posts = await this._context.Posts + .Where(post => post.TimeCreated < firstRequestIssued) + .Where(p => p.Creator.Id == user.Id) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + // Look at GetFriendsPosts on why this is done like this + posts = posts.OrderByDescending(x => x.TimeCreated.ToFileTime()).ToList(); return posts; } } diff --git a/src/DevHive.Data/Repositories/PostRepository.cs b/src/DevHive.Data/Repositories/PostRepository.cs index 78b40cd..07b5875 100644 --- a/src/DevHive.Data/Repositories/PostRepository.cs +++ b/src/DevHive.Data/Repositories/PostRepository.cs @@ -23,6 +23,7 @@ namespace DevHive.Data.Repositories { return await this._context.Posts .Include(x => x.Comments) + .Include(x => x.Creator) .FirstOrDefaultAsync(x => x.Id == id); } @@ -39,6 +40,30 @@ namespace DevHive.Data.Repositories } #endregion + #region Update + public override async Task<bool> EditAsync(Guid id, Post newEntity) + { + Post post = await this.GetByIdAsync(id); + + this._context + .Entry(post) + .CurrentValues + .SetValues(newEntity); + + post.FileUrls.Clear(); + foreach(var fileUrl in newEntity.FileUrls) + post.FileUrls.Add(fileUrl); + + post.Comments.Clear(); + foreach(var comment in newEntity.Comments) + post.Comments.Add(comment); + + this._context.Entry(post).State = EntityState.Modified; + + return await this.SaveChangesAsync(this._context); + } + #endregion + #region Validations public async Task<bool> DoesPostExist(Guid postId) { diff --git a/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs b/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs index ac3c8f6..a43b64e 100644 --- a/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs +++ b/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs @@ -1,6 +1,6 @@ using DevHive.Data.Models; using AutoMapper; -using DevHive.Services.Models.Post.Comment; +using DevHive.Services.Models.Comment; namespace DevHive.Services.Configurations.Mapping { diff --git a/src/DevHive.Services/Configurations/Mapping/PostMappings.cs b/src/DevHive.Services/Configurations/Mapping/PostMappings.cs index c7466d9..d36dbdd 100644 --- a/src/DevHive.Services/Configurations/Mapping/PostMappings.cs +++ b/src/DevHive.Services/Configurations/Mapping/PostMappings.cs @@ -1,6 +1,6 @@ using DevHive.Data.Models; using AutoMapper; -using DevHive.Services.Models.Post.Post; +using DevHive.Services.Models.Post; namespace DevHive.Services.Configurations.Mapping { @@ -17,10 +17,9 @@ namespace DevHive.Services.Configurations.Mapping CreateMap<Post, ReadPostServiceModel>() .ForMember(dest => dest.PostId, src => src.MapFrom(p => p.Id)) - .ForMember(dest => dest.CreatorFirstName, src => src.Ignore()) - .ForMember(dest => dest.CreatorLastName, src => src.Ignore()) - .ForMember(dest => dest.CreatorUsername, src => src.Ignore()); - //TODO: Map those here /\ + .ForMember(dest => dest.CreatorFirstName, src => src.MapFrom(p => p.Creator.FirstName)) + .ForMember(dest => dest.CreatorLastName, src => src.MapFrom(p => p.Creator.LastName)) + .ForMember(dest => dest.CreatorUsername, src => src.MapFrom(p => p.Creator.UserName)); } } } diff --git a/src/DevHive.Services/Interfaces/ICommentService.cs b/src/DevHive.Services/Interfaces/ICommentService.cs new file mode 100644 index 0000000..e7409a8 --- /dev/null +++ b/src/DevHive.Services/Interfaces/ICommentService.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; +using DevHive.Services.Models.Comment; + +namespace DevHive.Services.Interfaces +{ + public interface ICommentService + { + Task<Guid> AddComment(CreateCommentServiceModel createPostServiceModel); + + Task<ReadCommentServiceModel> GetCommentById(Guid id); + + Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel); + + Task<bool> DeleteComment(Guid id); + + Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData); + Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData); + } +} diff --git a/src/DevHive.Services/Interfaces/IFeedService.cs b/src/DevHive.Services/Interfaces/IFeedService.cs index 1edba5a..b507b3b 100644 --- a/src/DevHive.Services/Interfaces/IFeedService.cs +++ b/src/DevHive.Services/Interfaces/IFeedService.cs @@ -6,5 +6,6 @@ namespace DevHive.Services.Interfaces public interface IFeedService { Task<ReadPageServiceModel> GetPage(GetPageServiceModel getPageServiceModel); + Task<ReadPageServiceModel> GetUserPage(GetPageServiceModel model); } } diff --git a/src/DevHive.Services/Interfaces/IPostService.cs b/src/DevHive.Services/Interfaces/IPostService.cs index 71b558c..d35acfd 100644 --- a/src/DevHive.Services/Interfaces/IPostService.cs +++ b/src/DevHive.Services/Interfaces/IPostService.cs @@ -1,26 +1,20 @@ using System; using System.Threading.Tasks; -using DevHive.Services.Models.Post.Comment; -using DevHive.Services.Models.Post.Post; +using DevHive.Services.Models.Post; namespace DevHive.Services.Interfaces { - public interface IPostService + public interface IPostService { Task<Guid> CreatePost(CreatePostServiceModel createPostServiceModel); - Task<Guid> AddComment(CreateCommentServiceModel createPostServiceModel); Task<ReadPostServiceModel> GetPostById(Guid id); - Task<ReadCommentServiceModel> GetCommentById(Guid id); Task<Guid> UpdatePost(UpdatePostServiceModel updatePostServiceModel); - Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel); Task<bool> DeletePost(Guid id); - Task<bool> DeleteComment(Guid id); Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData); Task<bool> ValidateJwtForPost(Guid postId, string rawTokenData); - Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData); } } diff --git a/src/DevHive.Services/Models/Post/Comment/CreateCommentServiceModel.cs b/src/DevHive.Services/Models/Comment/CreateCommentServiceModel.cs index 8d49659..30e919b 100644 --- a/src/DevHive.Services/Models/Post/Comment/CreateCommentServiceModel.cs +++ b/src/DevHive.Services/Models/Comment/CreateCommentServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Post.Comment +namespace DevHive.Services.Models.Comment { public class CreateCommentServiceModel { diff --git a/src/DevHive.Services/Models/Post/Comment/ReadCommentServiceModel.cs b/src/DevHive.Services/Models/Comment/ReadCommentServiceModel.cs index 12e29a0..3196233 100644 --- a/src/DevHive.Services/Models/Post/Comment/ReadCommentServiceModel.cs +++ b/src/DevHive.Services/Models/Comment/ReadCommentServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Post.Comment +namespace DevHive.Services.Models.Comment { public class ReadCommentServiceModel { diff --git a/src/DevHive.Services/Models/Post/Comment/UpdateCommentServiceModel.cs b/src/DevHive.Services/Models/Comment/UpdateCommentServiceModel.cs index 3827d4d..3b78200 100644 --- a/src/DevHive.Services/Models/Post/Comment/UpdateCommentServiceModel.cs +++ b/src/DevHive.Services/Models/Comment/UpdateCommentServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Post.Comment +namespace DevHive.Services.Models.Comment { public class UpdateCommentServiceModel { diff --git a/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs b/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs index f291de7..95f6845 100644 --- a/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs +++ b/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using DevHive.Services.Models.Post.Post; +using DevHive.Services.Models.Post; namespace DevHive.Services.Models { diff --git a/src/DevHive.Services/Models/Post/Post/CreatePostServiceModel.cs b/src/DevHive.Services/Models/Post/CreatePostServiceModel.cs index 8676f6c..304eb90 100644 --- a/src/DevHive.Services/Models/Post/Post/CreatePostServiceModel.cs +++ b/src/DevHive.Services/Models/Post/CreatePostServiceModel.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Http; -namespace DevHive.Services.Models.Post.Post +namespace DevHive.Services.Models.Post { public class CreatePostServiceModel { diff --git a/src/DevHive.Services/Models/Post/Post/ReadPostServiceModel.cs b/src/DevHive.Services/Models/Post/ReadPostServiceModel.cs index f0a4fe5..04ec6bd 100644 --- a/src/DevHive.Services/Models/Post/Post/ReadPostServiceModel.cs +++ b/src/DevHive.Services/Models/Post/ReadPostServiceModel.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using DevHive.Services.Models.Post.Comment; +using DevHive.Services.Models.Comment; using Microsoft.Extensions.FileProviders; -namespace DevHive.Services.Models.Post.Post +namespace DevHive.Services.Models.Post { public class ReadPostServiceModel { diff --git a/src/DevHive.Services/Models/Post/Post/UpdatePostServiceModel.cs b/src/DevHive.Services/Models/Post/UpdatePostServiceModel.cs index 24b0b74..51b16bc 100644 --- a/src/DevHive.Services/Models/Post/Post/UpdatePostServiceModel.cs +++ b/src/DevHive.Services/Models/Post/UpdatePostServiceModel.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Http; -namespace DevHive.Services.Models.Post.Post +namespace DevHive.Services.Models.Post { public class UpdatePostServiceModel { diff --git a/src/DevHive.Services/Services/CommentService.cs b/src/DevHive.Services/Services/CommentService.cs new file mode 100644 index 0000000..e0eb88a --- /dev/null +++ b/src/DevHive.Services/Services/CommentService.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Models; +using DevHive.Services.Models.Comment; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using DevHive.Services.Interfaces; +using DevHive.Data.Interfaces.Repositories; +using System.Linq; + +namespace DevHive.Services.Services +{ + public class CommentService : ICommentService + { + private readonly IUserRepository _userRepository; + private readonly IPostRepository _postRepository; + private readonly ICommentRepository _commentRepository; + private readonly IMapper _postMapper; + + public CommentService(IUserRepository userRepository, IPostRepository postRepository, ICommentRepository commentRepository, IMapper postMapper) + { + this._userRepository = userRepository; + this._postRepository = postRepository; + this._commentRepository = commentRepository; + this._postMapper = postMapper; + } + + #region Create + public async Task<Guid> AddComment(CreateCommentServiceModel createCommentServiceModel) + { + if (!await this._postRepository.DoesPostExist(createCommentServiceModel.PostId)) + throw new ArgumentException("Post does not exist!"); + + Comment comment = this._postMapper.Map<Comment>(createCommentServiceModel); + comment.TimeCreated = DateTime.Now; + + comment.Creator = await this._userRepository.GetByIdAsync(createCommentServiceModel.CreatorId); + comment.Post = await this._postRepository.GetByIdAsync(createCommentServiceModel.PostId); + + bool success = await this._commentRepository.AddAsync(comment); + if (success) + { + Comment newComment = await this._commentRepository + .GetCommentByIssuerAndTimeCreatedAsync(comment.Creator.Id, comment.TimeCreated); + + return newComment.Id; + } + else + return Guid.Empty; + } + #endregion + + #region Read + public async Task<ReadCommentServiceModel> GetCommentById(Guid id) + { + Comment comment = await this._commentRepository.GetByIdAsync(id) ?? + throw new ArgumentException("The comment does not exist"); + + User user = await this._userRepository.GetByIdAsync(comment.Creator.Id) ?? + throw new ArgumentException("The user does not exist"); + + ReadCommentServiceModel readCommentServiceModel = this._postMapper.Map<ReadCommentServiceModel>(comment); + readCommentServiceModel.IssuerFirstName = user.FirstName; + readCommentServiceModel.IssuerLastName = user.LastName; + readCommentServiceModel.IssuerUsername = user.UserName; + + return readCommentServiceModel; + } + #endregion + + #region Update + public async Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel) + { + if (!await this._commentRepository.DoesCommentExist(updateCommentServiceModel.CommentId)) + throw new ArgumentException("Comment does not exist!"); + + Comment comment = this._postMapper.Map<Comment>(updateCommentServiceModel); + comment.TimeCreated = DateTime.Now; + + comment.Creator = await this._userRepository.GetByIdAsync(updateCommentServiceModel.CreatorId); + comment.Post = await this._postRepository.GetByIdAsync(updateCommentServiceModel.PostId); + + bool result = await this._commentRepository.EditAsync(updateCommentServiceModel.CommentId, comment); + + if (result) + return (await this._commentRepository.GetByIdAsync(updateCommentServiceModel.CommentId)).Id; + else + return Guid.Empty; + } + #endregion + + #region Delete + public async Task<bool> DeleteComment(Guid id) + { + if (!await this._commentRepository.DoesCommentExist(id)) + throw new ArgumentException("Comment does not exist!"); + + Comment comment = await this._commentRepository.GetByIdAsync(id); + return await this._commentRepository.DeleteAsync(comment); + } + #endregion + + #region Validations + public async Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData) + { + User user = await this.GetUserForValidation(rawTokenData); + + return user.Id == userId; + } + + public async Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData) + { + Comment comment = await this._commentRepository.GetByIdAsync(commentId) ?? + throw new ArgumentException("Comment does not exist!"); + User user = await this.GetUserForValidation(rawTokenData); + + //If user made the comment + if (comment.Creator.Id == user.Id) + return true; + //If user is admin + else if (user.Roles.Any(x => x.Name == Role.AdminRole)) + return true; + else + return false; + } + + private async Task<User> GetUserForValidation(string rawTokenData) + { + JwtSecurityToken jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7)); + + Guid jwtUserId = Guid.Parse(this.GetClaimTypeValues("ID", jwt.Claims).First()); + //HashSet<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims); + + User user = await this._userRepository.GetByIdAsync(jwtUserId) ?? + throw new ArgumentException("User does not exist!"); + + return user; + } + + + private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) + { + List<string> toReturn = new(); + + foreach (var claim in claims) + if (claim.Type == type) + toReturn.Add(claim.Value); + + return toReturn; + } + #endregion + } +} + diff --git a/src/DevHive.Services/Services/FeedService.cs b/src/DevHive.Services/Services/FeedService.cs index c17861d..37d653c 100644 --- a/src/DevHive.Services/Services/FeedService.cs +++ b/src/DevHive.Services/Services/FeedService.cs @@ -7,7 +7,7 @@ using DevHive.Data.Interfaces.Repositories; using DevHive.Data.Models; using DevHive.Services.Interfaces; using DevHive.Services.Models; -using DevHive.Services.Models.Post.Post; +using DevHive.Services.Models.Post; namespace DevHive.Services.Services { @@ -54,5 +54,29 @@ namespace DevHive.Services.Services return readPageServiceModel; } + + public async Task<ReadPageServiceModel> GetUserPage(GetPageServiceModel model) { + User user = null; + + if (!string.IsNullOrEmpty(model.Username)) + user = await this._userRepository.GetByUsernameAsync(model.Username); + else + throw new ArgumentException("Invalid given data!"); + + if (user == null) + throw new ArgumentException("User doesn't exist!"); + + List<Post> posts = await this._feedRepository + .GetUsersPosts(user, model.FirstRequestIssued, model.PageNumber, model.PageSize); + + if (posts.Count <= 0) + throw new ArgumentException("User hasn't posted anything yet!"); + + ReadPageServiceModel readPageServiceModel = new(); + foreach (Post post in posts) + readPageServiceModel.Posts.Add(this._mapper.Map<ReadPostServiceModel>(post)); + + return readPageServiceModel; + } } } diff --git a/src/DevHive.Services/Services/PostService.cs b/src/DevHive.Services/Services/PostService.cs index 7ce7b58..0eaac94 100644 --- a/src/DevHive.Services/Services/PostService.cs +++ b/src/DevHive.Services/Services/PostService.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using DevHive.Data.Models; -using DevHive.Services.Models.Post.Comment; -using DevHive.Services.Models.Post.Post; +using DevHive.Services.Models.Post; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using DevHive.Services.Interfaces; @@ -13,7 +12,7 @@ using System.Linq; namespace DevHive.Services.Services { - public class PostService : IPostService + public class PostService : IPostService { private readonly ICloudService _cloudService; private readonly IUserRepository _userRepository; @@ -55,29 +54,6 @@ namespace DevHive.Services.Services else return Guid.Empty; } - - public async Task<Guid> AddComment(CreateCommentServiceModel createCommentServiceModel) - { - if (!await this._postRepository.DoesPostExist(createCommentServiceModel.PostId)) - throw new ArgumentException("Post does not exist!"); - - Comment comment = this._postMapper.Map<Comment>(createCommentServiceModel); - comment.TimeCreated = DateTime.Now; - - comment.Creator = await this._userRepository.GetByIdAsync(createCommentServiceModel.CreatorId); - comment.Post = await this._postRepository.GetByIdAsync(createCommentServiceModel.PostId); - - bool success = await this._commentRepository.AddAsync(comment); - if (success) - { - Comment newComment = await this._commentRepository - .GetCommentByIssuerAndTimeCreatedAsync(comment.Creator.Id, comment.TimeCreated); - - return newComment.Id; - } - else - return Guid.Empty; - } #endregion #region Read @@ -96,22 +72,6 @@ namespace DevHive.Services.Services return readPostServiceModel; } - - public async Task<ReadCommentServiceModel> GetCommentById(Guid id) - { - Comment comment = await this._commentRepository.GetByIdAsync(id) ?? - throw new ArgumentException("The comment does not exist"); - - User user = await this._userRepository.GetByIdAsync(comment.Creator.Id) ?? - throw new ArgumentException("The user does not exist"); - - ReadCommentServiceModel readCommentServiceModel = this._postMapper.Map<ReadCommentServiceModel>(comment); - readCommentServiceModel.IssuerFirstName = user.FirstName; - readCommentServiceModel.IssuerLastName = user.LastName; - readCommentServiceModel.IssuerUsername = user.UserName; - - return readCommentServiceModel; - } #endregion #region Update @@ -146,25 +106,6 @@ namespace DevHive.Services.Services else return Guid.Empty; } - - public async Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel) - { - if (!await this._commentRepository.DoesCommentExist(updateCommentServiceModel.CommentId)) - throw new ArgumentException("Comment does not exist!"); - - Comment comment = this._postMapper.Map<Comment>(updateCommentServiceModel); - comment.TimeCreated = DateTime.Now; - - comment.Creator = await this._userRepository.GetByIdAsync(updateCommentServiceModel.CreatorId); - comment.Post = await this._postRepository.GetByIdAsync(updateCommentServiceModel.PostId); - - bool result = await this._commentRepository.EditAsync(updateCommentServiceModel.CommentId, comment); - - if (result) - return (await this._commentRepository.GetByIdAsync(updateCommentServiceModel.CommentId)).Id; - else - return Guid.Empty; - } #endregion #region Delete @@ -185,15 +126,6 @@ namespace DevHive.Services.Services return await this._postRepository.DeleteAsync(post); } - - public async Task<bool> DeleteComment(Guid id) - { - if (!await this._commentRepository.DoesCommentExist(id)) - throw new ArgumentException("Comment does not exist!"); - - Comment comment = await this._commentRepository.GetByIdAsync(id); - return await this._commentRepository.DeleteAsync(comment); - } #endregion #region Validations diff --git a/src/DevHive.Services/Services/UserService.cs b/src/DevHive.Services/Services/UserService.cs index ea53f1a..c2c42e0 100644 --- a/src/DevHive.Services/Services/UserService.cs +++ b/src/DevHive.Services/Services/UserService.cs @@ -53,7 +53,7 @@ namespace DevHive.Services.Services if (user.PasswordHash != PasswordModifications.GeneratePasswordHash(loginModel.Password)) throw new ArgumentException("Incorrect password!"); - return new TokenModel(WriteJWTSecurityToken(user.Id, user.Roles)); + return new TokenModel(WriteJWTSecurityToken(user.Id, user.UserName, user.Roles)); } public async Task<TokenModel> RegisterUser(RegisterServiceModel registerModel) @@ -78,7 +78,7 @@ namespace DevHive.Services.Services await this._userRepository.AddAsync(user); - return new TokenModel(WriteJWTSecurityToken(user.Id, user.Roles)); + return new TokenModel(WriteJWTSecurityToken(user.Id, user.UserName, user.Roles)); } #endregion @@ -107,8 +107,6 @@ namespace DevHive.Services.Services { await this.ValidateUserOnUpdate(updateUserServiceModel); - await this.ValidateUserCollections(updateUserServiceModel); - User user = await this.PopulateModel(updateUserServiceModel); bool successful = await this._userRepository.EditAsync(updateUserServiceModel.Id, user); @@ -190,62 +188,13 @@ namespace DevHive.Services.Services throw new ArgumentException("Username already exists!"); } - private async Task ValidateUserCollections(UpdateUserServiceModel updateUserServiceModel) - { - //Do NOT allow a user to change his roles, unless he is an Admin - bool isAdmin = (await this._userRepository.GetByIdAsync(updateUserServiceModel.Id)) - .Roles.Any(r => r.Name == Role.AdminRole); - - if (isAdmin) - { - // Roles - foreach (var role in updateUserServiceModel.Roles) - { - Role returnedRole = await this._roleRepository.GetByNameAsync(role.Name) ?? - throw new ArgumentException($"Role {role.Name} does not exist!"); - } - } - //Preserve original user roles - else - { - HashSet<Role> roles = (await this._userRepository.GetByIdAsync(updateUserServiceModel.Id)).Roles; - - foreach (var role in roles) - { - Role returnedRole = await this._roleRepository.GetByNameAsync(role.Name) ?? - throw new ArgumentException($"Role {role.Name} does not exist!"); - } - } - - // Friends - foreach (var friend in updateUserServiceModel.Friends) - { - User returnedFriend = await this._userRepository.GetByUsernameAsync(friend.UserName) ?? - throw new ArgumentException($"User {friend.UserName} does not exist!"); - } - - // Languages - foreach (var language in updateUserServiceModel.Languages) - { - Language returnedLanguage = await this._languageRepository.GetByNameAsync(language.Name) ?? - throw new ArgumentException($"Language {language.Name} does not exist!"); - } - - // Technology - foreach (var technology in updateUserServiceModel.Technologies) - { - Technology returnedTechnology = await this._technologyRepository.GetByNameAsync(technology.Name) ?? - throw new ArgumentException($"Technology {technology.Name} does not exist!"); - } - } - - private string WriteJWTSecurityToken(Guid userId, HashSet<Role> roles) + private string WriteJWTSecurityToken(Guid userId, string username, HashSet<Role> roles) { byte[] signingKey = Encoding.ASCII.GetBytes(_jwtOptions.Secret); - HashSet<Claim> claims = new() { new Claim("ID", $"{userId}"), + new Claim("Username", username), }; foreach (var role in roles) @@ -269,12 +218,12 @@ namespace DevHive.Services.Services #endregion #region Misc - public async Task<Guid> SuperSecretPromotionToAdmin(Guid userId) + public async Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId) { User user = await this._userRepository.GetByIdAsync(userId) ?? throw new ArgumentException("User does not exist! Can't promote shit in this country..."); - if (!await this._roleRepository.DoesNameExist("Admin")) + if (!await this._roleRepository.DoesNameExist(Role.AdminRole)) { Role adminRole = new() { @@ -290,7 +239,9 @@ namespace DevHive.Services.Services user.Roles.Add(admin); await this._userRepository.EditAsync(user.Id, user); - return admin.Id; + User newUser = await this._userRepository.GetByIdAsync(userId); + + return new TokenModel(WriteJWTSecurityToken(newUser.Id, newUser.UserName, newUser.Roles); } private async Task<User> PopulateModel(UpdateUserServiceModel updateUserServiceModel) diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs b/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs index 46e67ee..22df311 100644 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs +++ b/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.DependencyInjection; namespace DevHive.Web.Configurations.Extensions { - public static class ConfigureDependencyInjection + public static class ConfigureDependencyInjection { public static void DependencyInjectionConfiguration(this IServiceCollection services, IConfiguration configuration) { @@ -25,6 +25,7 @@ namespace DevHive.Web.Configurations.Extensions services.AddTransient<ITechnologyService, TechnologyService>(); services.AddTransient<IUserService, UserService>(); services.AddTransient<IPostService, PostService>(); + services.AddTransient<ICommentService, CommentService>(); services.AddTransient<IFeedService, FeedService>(); services.AddTransient<ICloudService, CloudinaryService>(options => new CloudinaryService( diff --git a/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs b/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs index a28ee16..b8d6829 100644 --- a/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs +++ b/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs @@ -1,6 +1,6 @@ using AutoMapper; -using DevHive.Services.Models.Post.Comment; -using DevHive.Web.Models.Post.Comment; +using DevHive.Services.Models.Comment; +using DevHive.Web.Models.Comment; namespace DevHive.Web.Configurations.Mapping { @@ -15,6 +15,3 @@ namespace DevHive.Web.Configurations.Mapping } } } - - - diff --git a/src/DevHive.Web/Configurations/Mapping/PostMappings.cs b/src/DevHive.Web/Configurations/Mapping/PostMappings.cs index bc7bc06..a5b46ee 100644 --- a/src/DevHive.Web/Configurations/Mapping/PostMappings.cs +++ b/src/DevHive.Web/Configurations/Mapping/PostMappings.cs @@ -1,6 +1,6 @@ using AutoMapper; -using DevHive.Services.Models.Post.Post; -using DevHive.Web.Models.Post.Post; +using DevHive.Services.Models.Post; +using DevHive.Web.Models.Post; namespace DevHive.Web.Configurations.Mapping { diff --git a/src/DevHive.Web/Controllers/CommentController.cs b/src/DevHive.Web/Controllers/CommentController.cs new file mode 100644 index 0000000..ebcb87a --- /dev/null +++ b/src/DevHive.Web/Controllers/CommentController.cs @@ -0,0 +1,82 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using AutoMapper; +using System; +using DevHive.Web.Models.Comment; +using DevHive.Services.Models.Comment; +using Microsoft.AspNetCore.Authorization; +using DevHive.Services.Interfaces; + +namespace DevHive.Web.Controllers +{ + [ApiController] + [Route("/api/[controller]")] + [Authorize(Roles = "User,Admin")] + public class CommentController { + private readonly ICommentService _commentService; + private readonly IMapper _commentMapper; + + public CommentController(ICommentService commentService, IMapper commentMapper) + { + this._commentService = commentService; + this._commentMapper = commentMapper; + } + + [HttpPost] + public async Task<IActionResult> AddComment(Guid userId, [FromBody] CreateCommentWebModel createCommentWebModel, [FromHeader] string authorization) + { + if (!await this._commentService.ValidateJwtForCreating(userId, authorization)) + return new UnauthorizedResult(); + + CreateCommentServiceModel createCommentServiceModel = + this._commentMapper.Map<CreateCommentServiceModel>(createCommentWebModel); + createCommentServiceModel.CreatorId = userId; + + Guid id = await this._commentService.AddComment(createCommentServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Could not create comment!") : + new OkObjectResult(new { Id = id }); + } + + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> GetCommentById(Guid id) + { + ReadCommentServiceModel readCommentServiceModel = await this._commentService.GetCommentById(id); + ReadCommentWebModel readCommentWebModel = this._commentMapper.Map<ReadCommentWebModel>(readCommentServiceModel); + + return new OkObjectResult(readCommentWebModel); + } + + [HttpPut] + public async Task<IActionResult> UpdateComment(Guid userId, [FromBody] UpdateCommentWebModel updateCommentWebModel, [FromHeader] string authorization) + { + if (!await this._commentService.ValidateJwtForComment(updateCommentWebModel.CommentId, authorization)) + return new UnauthorizedResult(); + + UpdateCommentServiceModel updateCommentServiceModel = + this._commentMapper.Map<UpdateCommentServiceModel>(updateCommentWebModel); + updateCommentServiceModel.CreatorId = userId; + + Guid id = await this._commentService.UpdateComment(updateCommentServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Unable to update comment!") : + new OkObjectResult(new { Id = id }); + } + + [HttpDelete] + public async Task<IActionResult> DeleteComment(Guid id, [FromHeader] string authorization) + { + if (!await this._commentService.ValidateJwtForComment(id, authorization)) + return new UnauthorizedResult(); + + return await this._commentService.DeleteComment(id) ? + new OkResult() : + new BadRequestObjectResult("Could not delete Comment"); + } + + } +} + diff --git a/src/DevHive.Web/Controllers/FeedController.cs b/src/DevHive.Web/Controllers/FeedController.cs index 4fd3ae9..2f14cf3 100644 --- a/src/DevHive.Web/Controllers/FeedController.cs +++ b/src/DevHive.Web/Controllers/FeedController.cs @@ -23,7 +23,7 @@ namespace DevHive.Web.Controllers this._mapper = mapper; } - [HttpGet] + [HttpPost] [Route("GetPosts")] public async Task<IActionResult> GetPosts(Guid userId, [FromBody] GetPageWebModel getPageWebModel) { @@ -36,14 +36,15 @@ namespace DevHive.Web.Controllers return new OkObjectResult(readPageWebModel); } - [HttpGet] + [HttpPost] [Route("GetUserPosts")] + [AllowAnonymous] public async Task<IActionResult> GetUserPosts(string username, [FromBody] GetPageWebModel getPageWebModel) { GetPageServiceModel getPageServiceModel = this._mapper.Map<GetPageServiceModel>(getPageWebModel); getPageServiceModel.Username = username; - ReadPageServiceModel readPageServiceModel = await this._feedService.GetPage(getPageServiceModel); + ReadPageServiceModel readPageServiceModel = await this._feedService.GetUserPage(getPageServiceModel); ReadPageWebModel readPageWebModel = this._mapper.Map<ReadPageWebModel>(readPageServiceModel); return new OkObjectResult(readPageWebModel); diff --git a/src/DevHive.Web/Controllers/PostController.cs b/src/DevHive.Web/Controllers/PostController.cs index fe71519..53adfce 100644 --- a/src/DevHive.Web/Controllers/PostController.cs +++ b/src/DevHive.Web/Controllers/PostController.cs @@ -2,16 +2,14 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using AutoMapper; using System; -using DevHive.Web.Models.Post.Post; -using DevHive.Services.Models.Post.Post; -using DevHive.Web.Models.Post.Comment; -using DevHive.Services.Models.Post.Comment; +using DevHive.Web.Models.Post; +using DevHive.Services.Models.Post; using Microsoft.AspNetCore.Authorization; using DevHive.Services.Interfaces; namespace DevHive.Web.Controllers { - [ApiController] + [ApiController] [Route("/api/[controller]")] [Authorize(Roles = "User,Admin")] public class PostController @@ -42,24 +40,6 @@ namespace DevHive.Web.Controllers new BadRequestObjectResult("Could not create post!") : new OkObjectResult(new { Id = id }); } - - [HttpPost] - [Route("Comment")] - public async Task<IActionResult> AddComment(Guid userId, [FromBody] CreateCommentWebModel createCommentWebModel, [FromHeader] string authorization) - { - if (!await this._postService.ValidateJwtForCreating(userId, authorization)) - return new UnauthorizedResult(); - - CreateCommentServiceModel createCommentServiceModel = - this._postMapper.Map<CreateCommentServiceModel>(createCommentWebModel); - createCommentServiceModel.CreatorId = userId; - - Guid id = await this._postService.AddComment(createCommentServiceModel); - - return id == Guid.Empty ? - new BadRequestObjectResult("Could not create comment!") : - new OkObjectResult(new { Id = id }); - } #endregion #region Read @@ -72,17 +52,6 @@ namespace DevHive.Web.Controllers return new OkObjectResult(postWebModel); } - - [HttpGet] - [Route("Comment")] - [AllowAnonymous] - public async Task<IActionResult> GetCommentById(Guid id) - { - ReadCommentServiceModel readCommentServiceModel = await this._postService.GetCommentById(id); - ReadCommentWebModel readCommentWebModel = this._postMapper.Map<ReadCommentWebModel>(readCommentServiceModel); - - return new OkObjectResult(readCommentWebModel); - } #endregion #region Update @@ -102,24 +71,6 @@ namespace DevHive.Web.Controllers new BadRequestObjectResult("Unable to update post!") : new OkObjectResult(new { Id = id }); } - - [HttpPut] - [Route("Comment")] - public async Task<IActionResult> UpdateComment(Guid userId, [FromBody] UpdateCommentWebModel updateCommentWebModel, [FromHeader] string authorization) - { - if (!await this._postService.ValidateJwtForComment(updateCommentWebModel.CommentId, authorization)) - return new UnauthorizedResult(); - - UpdateCommentServiceModel updateCommentServiceModel = - this._postMapper.Map<UpdateCommentServiceModel>(updateCommentWebModel); - updateCommentServiceModel.CreatorId = userId; - - Guid id = await this._postService.UpdateComment(updateCommentServiceModel); - - return id == Guid.Empty ? - new BadRequestObjectResult("Unable to update comment!") : - new OkObjectResult(new { Id = id }); - } #endregion #region Delete @@ -133,18 +84,6 @@ namespace DevHive.Web.Controllers new OkResult() : new BadRequestObjectResult("Could not delete Comment"); } - - [HttpDelete] - [Route("Comment")] - public async Task<IActionResult> DeleteComment(Guid id, [FromHeader] string authorization) - { - if (!await this._postService.ValidateJwtForComment(id, authorization)) - return new UnauthorizedResult(); - - return await this._postService.DeleteComment(id) ? - new OkResult() : - new BadRequestObjectResult("Could not delete Comment"); - } #endregion } } diff --git a/src/DevHive.Web/Models/Post/Comment/CreateCommentWebModel.cs b/src/DevHive.Web/Models/Comment/CreateCommentWebModel.cs index 85c67bf..8b2bf8d 100644 --- a/src/DevHive.Web/Models/Post/Comment/CreateCommentWebModel.cs +++ b/src/DevHive.Web/Models/Comment/CreateCommentWebModel.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace DevHive.Web.Models.Post.Comment +namespace DevHive.Web.Models.Comment { public class CreateCommentWebModel { diff --git a/src/DevHive.Web/Models/Post/Comment/ReadCommentWebModel.cs b/src/DevHive.Web/Models/Comment/ReadCommentWebModel.cs index 5320c3c..4d3aff7 100644 --- a/src/DevHive.Web/Models/Post/Comment/ReadCommentWebModel.cs +++ b/src/DevHive.Web/Models/Comment/ReadCommentWebModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Web.Models.Post.Comment +namespace DevHive.Web.Models.Comment { public class ReadCommentWebModel { diff --git a/src/DevHive.Web/Models/Post/Comment/UpdateCommentWebModel.cs b/src/DevHive.Web/Models/Comment/UpdateCommentWebModel.cs index cb1c60a..b5d7970 100644 --- a/src/DevHive.Web/Models/Post/Comment/UpdateCommentWebModel.cs +++ b/src/DevHive.Web/Models/Comment/UpdateCommentWebModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Web.Models.Post.Comment +namespace DevHive.Web.Models.Comment { public class UpdateCommentWebModel { diff --git a/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs b/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs index 40d29c9..839aaa6 100644 --- a/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs +++ b/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using DevHive.Web.Models.Post.Post; +using DevHive.Web.Models.Post; namespace DevHive.Web.Controllers { diff --git a/src/DevHive.Web/Models/Post/Post/CreatePostWebModel.cs b/src/DevHive.Web/Models/Post/CreatePostWebModel.cs index e35a813..256055a 100644 --- a/src/DevHive.Web/Models/Post/Post/CreatePostWebModel.cs +++ b/src/DevHive.Web/Models/Post/CreatePostWebModel.cs @@ -1,12 +1,11 @@ -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; -namespace DevHive.Web.Models.Post.Post +namespace DevHive.Web.Models.Post { - public class CreatePostWebModel + public class CreatePostWebModel { [NotNull] [Required] diff --git a/src/DevHive.Web/Models/Post/Post/ReadPostWebModel.cs b/src/DevHive.Web/Models/Post/ReadPostWebModel.cs index 5d4da31..1d2669e 100644 --- a/src/DevHive.Web/Models/Post/Post/ReadPostWebModel.cs +++ b/src/DevHive.Web/Models/Post/ReadPostWebModel.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using DevHive.Web.Models.Post.Comment; +using DevHive.Web.Models.Comment; using Microsoft.AspNetCore.Http; -namespace DevHive.Web.Models.Post.Post +namespace DevHive.Web.Models.Post { public class ReadPostWebModel { diff --git a/src/DevHive.Web/Models/Post/Post/UpdatePostWebModel.cs b/src/DevHive.Web/Models/Post/UpdatePostWebModel.cs index ac84d2c..a0c9b61 100644 --- a/src/DevHive.Web/Models/Post/Post/UpdatePostWebModel.cs +++ b/src/DevHive.Web/Models/Post/UpdatePostWebModel.cs @@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; -namespace DevHive.Web.Models.Post.Post +namespace DevHive.Web.Models.Post { public class UpdatePostWebModel { |
