Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [community/profiles] ajoute un badge sur la side bar #308

Merged
merged 2 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dynonuggets.refonteimplicaction.community.profile.controller;

import com.dynonuggets.refonteimplicaction.auth.service.AuthService;
import com.dynonuggets.refonteimplicaction.community.profile.dto.ProfileDto;
import com.dynonuggets.refonteimplicaction.community.profile.dto.ProfileUpdateRequest;
import com.dynonuggets.refonteimplicaction.community.profile.dto.enums.RelationCriteriaEnum;
Expand All @@ -25,6 +26,7 @@
public class ProfileController {

private final ProfileService profileService;
private final AuthService authService;

@GetMapping
@ResponseBody
Expand All @@ -33,7 +35,7 @@ public ResponseEntity<Page<ProfileDto>> getAllProfiles(
@RequestParam(defaultValue = "10") final int rows,
@RequestParam(value = "sortBy", defaultValue = "id") final String[] sortBy,
@RequestParam(value = "sortOrder", defaultValue = "ASC") final String sortOrder,
@RequestParam(value = "relationCriteria", defaultValue = "ANY") final RelationCriteriaEnum relationCriteria
@RequestParam(value = "relationCriteria", defaultValue = "ALL") final RelationCriteriaEnum relationCriteria
) {
final Pageable pageable = PageRequest.of(page, rows, Sort.by(Sort.Direction.valueOf(sortOrder), sortBy));
return ResponseEntity.ok(profileService.getAllProfiles(relationCriteria, pageable));
Expand All @@ -53,4 +55,9 @@ public ResponseEntity<ProfileDto> getProfileByUsername(@PathVariable final Strin
public ResponseEntity<ProfileDto> postProfileAvatar(@RequestParam final MultipartFile file, @PathVariable final String username) {
return ResponseEntity.ok(profileService.uploadAvatar(file, username));
}

@GetMapping("/friend-requests/count")
public Long getCountFriendRequestsByUsername() {
return profileService.countFriendRequestsByUsername(authService.getCurrentUser().getUsername());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public interface ProfileRepositoryCustom {

Page<ProfileModel> findAllProfilesWithRelationTypeCriteria(final String username, final RelationCriteriaEnum relationCriteria, final Pageable pageable);

Long countFriendRequestsByUsername(String username);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import com.dynonuggets.refonteimplicaction.community.profile.domain.repository.ProfileRepositoryCustom;
import com.dynonuggets.refonteimplicaction.community.profile.dto.enums.RelationCriteriaEnum;
import com.dynonuggets.refonteimplicaction.community.relation.domain.model.RelationModel;
import com.dynonuggets.refonteimplicaction.job.company.domain.model.CompanyModel;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.criteria.*;
Expand All @@ -23,38 +23,58 @@ public class ProfileRepositoryCustomImpl implements ProfileRepositoryCustom {
private final EntityManager entityManager;

/**
* @param username nom de l'utilisateur courant
* @param username nom de lutilisateur courant
* @param relationCriteria type de relation.
* @param pageable options de pagination
* @return <ul>
* <li>{@link ANY} retourne tous profils qu'ils soient en relation ou non avec l'utilisateur courant à l'exception de l'utilisateur courant</li>
* <li>{@link ALL_FRIENDS} retourne toutes les relations confirmées de l'utilisateur (en tant que sender ou receiver) à l'exception de l'utilisateur courant</li>
* <li>{@link ONLY_FRIEND_REQUESTS} retourne seulement les profiles qui ont effectué une demande de relation avec l'utilisateur courant à l'exception de l'utilisateur courant</li>
* <li>{@link RelationCriteriaEnum#ALL} retourne tous profils qu'ils soient en relation ou non avec l'utilisateur courant à l'exception de l'utilisateur courant</li>
* <li>{@link RelationCriteriaEnum#ALL_FRIENDS} retourne toutes les relations confirmées de l'utilisateur (en tant que sender ou receiver) à l'exception de l'utilisateur courant</li>
* <li>{@link RelationCriteriaEnum#ONLY_FRIEND_REQUESTS} retourne seulement les profiles qui ont effectué une demande de relation avec l'utilisateur courant à l'exception de l'utilisateur courant</li>
*/
@Override
public Page<ProfileModel> findAllProfilesWithRelationTypeCriteria(final String username, final RelationCriteriaEnum relationCriteria, final Pageable pageable) {
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<ProfileModel> query = builder.createQuery(ProfileModel.class);
final Root<ProfileModel> p = query.from(ProfileModel.class);
final List<Predicate> queryPredicates = new ArrayList<>();
final Predicate[] queryPredicates = generateFilterPredicates(username, relationCriteria, query);

query.where(queryPredicates);

final CriteriaQuery<Long> countQuery = builder.createQuery(Long.class).where(queryPredicates);
countQuery.select(builder.count(countQuery.from(ProfileModel.class)));
final List<ProfileModel> profileModels = entityManager.createQuery(query)
.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
.getResultList();

return PageableExecutionUtils.getPage(profileModels, pageable, () -> entityManager.createQuery(countQuery).getSingleResult());
}

@Override
@Transactional(readOnly = true)
public Long countFriendRequestsByUsername(final String username) {
return findAllProfilesWithRelationTypeCriteria(username, ONLY_FRIEND_REQUESTS, Pageable.ofSize(Integer.MAX_VALUE)).getTotalElements();
}

queryPredicates.add(builder.isTrue(p.get("user").get("enabled")));
queryPredicates.add(builder.notEqual(p.get("user").get("username"), username));
private Predicate[] generateFilterPredicates(final String username, final RelationCriteriaEnum relationCriteria, final CriteriaQuery<?> query) {
final Root<ProfileModel> p = query.from(ProfileModel.class);
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final List<Predicate> queryPredicates = new ArrayList<>() {{
add(builder.isTrue(p.get("user").get("enabled")));
add(builder.notEqual(p.get("user").get("username"), username));
}};

if (List.of(ALL_FRIENDS, ONLY_FRIEND_REQUESTS).contains(relationCriteria)) {
final Subquery<Integer> subQuery = query.subquery(Integer.class);
final Root<RelationModel> r = subQuery.from(RelationModel.class);

// l'utilisateur doit être à la cible d'une invitation (receiver)
// l'utilisateur doit être à la cible dune invitation (receiver)
Predicate relationPredicate = builder.and(builder.equal(r.get("receiver").get("user").get("username"), username), builder.equal(r.get("sender"), p));

// si l'on veut remonter toutes les relatons de l'utilisateur, il faut aussi récupérer les profils pour lesquels il est à l'origine d'une relation
// si lon veut remonter toutes les relatons de lutilisateur, il faut aussi récupérer les profils pour lesquels il est à lorigine dune relation
if (relationCriteria == ALL_FRIENDS) {
final Predicate isSender = builder.and(builder.equal(r.get("sender").get("user").get("username"), username), builder.equal(r.get("receiver"), p));
relationPredicate = builder.or(relationPredicate, isSender);
}

// si 2 utilisateurs sont amis, alors la relation a été confirmée, sinon non
final Predicate confirmedPredicate = relationCriteria == ALL_FRIENDS
? builder.isNotNull(r.get("confirmedAt"))
: builder.isNull(r.get("confirmedAt"));
Expand All @@ -65,15 +85,7 @@ public Page<ProfileModel> findAllProfilesWithRelationTypeCriteria(final String u

queryPredicates.add(builder.exists(subQuery));
}

query.where(queryPredicates.toArray(Predicate[]::new));

final CriteriaQuery<Long> countQuery = builder.createQuery(Long.class).where(queryPredicates.toArray(Predicate[]::new));
countQuery.select(builder.count(countQuery.from(CompanyModel.class)));
final List<ProfileModel> profileModels = entityManager.createQuery(query)
.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
.getResultList();

return PageableExecutionUtils.getPage(profileModels, pageable, () -> entityManager.createQuery(countQuery).getSingleResult());
return queryPredicates.toArray(Predicate[]::new);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dynonuggets.refonteimplicaction.community.profile.dto.enums;

public enum RelationCriteriaEnum {
ANY, ALL_FRIENDS, ONLY_FRIEND_REQUESTS
ALL,
ALL_FRIENDS,
ONLY_FRIEND_REQUESTS
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ public Page<ProfileDto> getAllProfiles(final RelationCriteriaEnum relationCriter
});
}

public Long countFriendRequestsByUsername(final String username) {
return profileRepository.countFriendRequestsByUsername(username);
}

/**
* Renvoie le profil s’il existe, sinon, si l’utilisateur existe, le profil est créé
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dynonuggets.refonteimplicaction.community.profile.controller;

import com.dynonuggets.refonteimplicaction.auth.service.AuthService;
import com.dynonuggets.refonteimplicaction.community.profile.dto.ProfileDto;
import com.dynonuggets.refonteimplicaction.community.profile.dto.ProfileUpdateRequest;
import com.dynonuggets.refonteimplicaction.community.profile.dto.enums.RelationCriteriaEnum;
Expand Down Expand Up @@ -51,6 +52,9 @@ class ProfileControllerTest extends ControllerIntegrationTestBase {
@Getter
protected String baseUri = PROFILES_BASE_URI;

@MockBean
AuthService authService;

@MockBean
ProfileService profileService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,19 @@ void should_get_all_profiles_when_getAllProfiles() {
final UserModel currentUser = generateRandomUserModel();
final Pageable pageable = Pageable.ofSize(10);
final List<String> usernames = profileModels.stream().map(p -> p.getUser().getUsername()).collect(Collectors.toList());

given(authService.getCurrentUser()).willReturn(currentUser);
given(profileRepository.findAllProfilesWithRelationTypeCriteria(currentUser.getUsername(), RelationCriteriaEnum.ANY, pageable)).willReturn(profiles);
given(profileRepository.findAllProfilesWithRelationTypeCriteria(currentUser.getUsername(), RelationCriteriaEnum.ALL, pageable)).willReturn(profiles);
given(relationRepository.findAllRelationByUsernameWhereUserListAreSenderOrReceiver(currentUser.getUsername(), usernames)).willReturn(of());
profiles.forEach(p -> given(profileMapper.toDtoLight(p)).willReturn(ProfileDto.builder().username(p.getUser().getUsername()).build()));

// when
final Page<ProfileDto> result = profileService.getAllProfiles(RelationCriteriaEnum.ANY, pageable);
final Page<ProfileDto> result = profileService.getAllProfiles(RelationCriteriaEnum.ALL, pageable);

// then
final int size = profiles.getSize();
assertThat(result).hasSize(size);
verify(profileRepository, times(1)).findAllProfilesWithRelationTypeCriteria(currentUser.getUsername(), RelationCriteriaEnum.ANY, pageable);
verify(profileRepository, times(1)).findAllProfilesWithRelationTypeCriteria(currentUser.getUsername(), RelationCriteriaEnum.ALL, pageable);
verify(profileMapper, times(size)).toDtoLight(any());
}

Expand Down
2 changes: 1 addition & 1 deletion frontend-implicaction/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {UnauthorizedPageComponent} from './auth/pages/unauthorized-page/unauthor

const routes: Routes = [
{
path: 'auth',
path: Univers.AUTH.url,
loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule)
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ import {ProfileListPageComponent} from "./pages/profile-list-page/profile-list-p
import {ProfileDetailsPageComponent} from "./pages/profile-details-page/profile-details-page.component";

const routes: Routes = [
{
path: 'relations/received',
component: ProfileListPageComponent,
},
{
path: 'relations/sent',
component: ProfileListPageComponent,
},
{
path: 'relations',
component: ProfileListPageComponent,
},
{
path: 'profiles/:username',
component: ProfileDetailsPageComponent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {EnumCodeLabelAbstract} from "../../../shared/enums/enum-code-label-abstract.enum";
import {Univers} from "../../../shared/enums/univers";

export enum ProfileMenuCode {
ALL = 'ALL',
ALL_FRIENDS = 'ALL_FRIENDS',
ONLY_FRIEND_REQUESTS = 'ONLY_FRIEND_REQUESTS'
}

export class ProfileMenuEnum extends EnumCodeLabelAbstract<ProfileMenuCode> {

static readonly ALL = new ProfileMenuEnum(ProfileMenuCode.ALL, 'Tous les utilisateurs', null, `/${Univers.COMMUNITY.url}/profiles`);
static readonly ALL_FRIENDS = new ProfileMenuEnum(ProfileMenuCode.ALL_FRIENDS, 'Mes amis', null, `/${Univers.COMMUNITY.url}/profiles`);
static readonly ONLY_FRIEND_REQUESTS = new ProfileMenuEnum(ProfileMenuCode.ONLY_FRIEND_REQUESTS, 'Invitations', null, `/${Univers.COMMUNITY.url}/profiles`);

constructor(readonly code: ProfileMenuCode, readonly label: string, readonly icon: string, readonly url: string) {
super(code, label);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import {Relation} from '../relation';
import {EnumCodeLabelAbstract} from '../../../shared/enums/enum-code-label-abstract.enum';

export enum RelationActionEnumCode {
CREATE = 'CREATE', DELETE = 'DELETE', CONFIRM = 'CONFIRM'
}

export interface RelationAction {
relation: Relation;
action: RelationActionEnumCode;
CREATE = 'CREATE',
DELETE = 'DELETE',
CONFIRM = 'CONFIRM'
}

export class RelationActionEnum extends EnumCodeLabelAbstract<RelationActionEnumCode> {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {Constants} from '../../../config/constants';
import {RelationActionEnumCode} from '../../models/enums/relation-action';
import {Relation} from '../../models/relation';
import {Subject} from 'rxjs';
import {RelationCriteriaEnum} from '../../models/enums/relation-criteria-enum';
import {RelationButton} from '../../models/relation-button';
import {ProfileMenuCode, ProfileMenuEnum} from "../../models/enums/profile-menu-enum";

@Component({
templateUrl: './profile-list-page.component.html',
Expand All @@ -30,28 +30,18 @@ export class ProfileListPageComponent implements OnInit, OnDestroy {
currentUser: User;
action: string;
loading = true;
relationTypeCriteria: RelationCriteriaEnum;
menuItems: MenuItem[] = [
{
label: 'Tous les utilisateurs',
routerLink: `/${Univers.COMMUNITY.url}/profiles`,
routerLinkActiveOptions: {exact: true}
},
{
label: 'Mes amis',
routerLink: `/${Univers.COMMUNITY.url}/profiles`,
queryParams: {filter: RelationCriteriaEnum.ALL_FRIENDS},
routerLinkActiveOptions: {exact: true}
},
{
label: 'Invitations',
routerLink: `/${Univers.COMMUNITY.url}/profiles`,
queryParams: {filter: RelationCriteriaEnum.ONLY_FRIEND_REQUESTS},
routerLinkActiveOptions: {exact: true}
}
];
selectedItemMenuCode: ProfileMenuCode;
menuItems: MenuItem[] = ProfileMenuEnum.values()
.map((item: ProfileMenuEnum) => ({
id: item.code,
label: item.label,
routerLink: item.url,
queryParams: {filter: item.code},
routerLinkActiveOptions: {queryParams: 'subset'}
} as MenuItem));

private onDestroySubject = new Subject<void>();
private onlyFriendRequestMenuItem: MenuItem;

constructor(
private authService: AuthService,
Expand All @@ -65,13 +55,20 @@ export class ProfileListPageComponent implements OnInit, OnDestroy {

ngOnInit(): void {
this.currentUser = this.authService.getPrincipal();
this.onlyFriendRequestMenuItem = this.menuItems.find(item => item.id === ProfileMenuCode.ONLY_FRIEND_REQUESTS);
this.route.queryParams
.pipe(takeUntil(this.onDestroySubject))
.subscribe(params => {
this.pageable.number = (params.page ?? 1) - 1;
this.relationTypeCriteria = params.filter ?? RelationCriteriaEnum.ANY;
this.selectedItemMenuCode = params.filter ?? ProfileMenuCode.ALL;
this.fetchProfiles();
});

this.profileService.getProfileRequestAsFriendCount().subscribe(relationCount => {
if (this.onlyFriendRequestMenuItem) {
this.onlyFriendRequestMenuItem.badge = relationCount ? `${relationCount}` : null;
}
});
}

trackByUsername = (index: number, item: Profile) => item.username;
Expand Down Expand Up @@ -130,11 +127,15 @@ export class ProfileListPageComponent implements OnInit, OnDestroy {
const index = this.pageable.content.findIndex(p => p.username === profile.username);
this.pageable.content.splice(index, 1, {...profile, relationWithCurrentUser});
this.profiles = [...this.pageable.content];
if (relationWithCurrentUser.confirmedAt) {
const relationCount = +this.onlyFriendRequestMenuItem.badge - 1;
this.onlyFriendRequestMenuItem.badge = relationCount ? `${relationCount}` : null;
}
}

private fetchProfiles(): void {
this.loading = true;
this.profileService.getAllProfiles(this.relationTypeCriteria ?? RelationCriteriaEnum.ANY, this.pageable)
this.profileService.getAllProfiles(this.selectedItemMenuCode ?? ProfileMenuCode.ALL, this.pageable)
.pipe(
finalize(() => this.loading = false),
take(1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {HttpClient, HttpEvent, HttpRequest} from '@angular/common/http';
import {ApiEndpointsService} from '../../../core/services/api-endpoints.service';
import {Profile} from '../../models/profile';
import {Pageable} from '../../../shared/models/pageable';
import {RelationCriteriaEnum} from '../../models/enums/relation-criteria-enum';
import {ProfileMenuCode} from "../../models/enums/profile-menu-enum";

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -37,7 +37,11 @@ export class ProfileService {
return this.http.request<Profile>(req);
}

getAllProfiles(relationCriteria: RelationCriteriaEnum, pageable: Pageable<Profile>): Observable<Pageable<Profile>> {
getAllProfiles(relationCriteria: ProfileMenuCode, pageable: Pageable<Profile>): Observable<Pageable<Profile>> {
return this.http.get<Pageable<Profile>>(this.apiEndpointsService.getAllProfilesEndpoint(relationCriteria, pageable));
}

getProfileRequestAsFriendCount(): Observable<number> {
return this.http.get<number>(this.apiEndpointsService.getProfileRequestAsFriendCountEndpoint(), {});
}
}
Loading