From bfbefd0973a4eda66f0b6e4f1d13e353a3de9b94 Mon Sep 17 00:00:00 2001 From: tmddus2 Date: Sat, 10 Aug 2024 15:53:28 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20tag=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- purithm/build.gradle | 26 ++++ .../filter/controller/FilterController.java | 3 +- .../controller/FilterControllerDocs.java | 3 +- .../purithm/domain/filter/entity/Filter.java | 2 + .../filter/entity/StringToTagConverter.java | 13 ++ .../purithm/domain/filter/entity/Tag.java | 35 +++--- .../repository/CustomFilterRepository.java | 14 +++ .../CustomFilterRepositoryImpl.java | 119 ++++++++++++++++++ .../filter/repository/FilterRepository.java | 24 +--- .../filter/repository/QuerydslConfig.java | 21 ++++ .../filter/repository/TagRepository.java | 17 --- .../domain/filter/service/FilterService.java | 79 +++++------- .../purithm/domain/review/entity/Review.java | 2 +- .../domain/review/service/ReviewService.java | 4 +- 14 files changed, 256 insertions(+), 106 deletions(-) create mode 100644 purithm/src/main/java/com/example/purithm/domain/filter/entity/StringToTagConverter.java create mode 100644 purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepository.java create mode 100644 purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepositoryImpl.java create mode 100644 purithm/src/main/java/com/example/purithm/domain/filter/repository/QuerydslConfig.java delete mode 100644 purithm/src/main/java/com/example/purithm/domain/filter/repository/TagRepository.java diff --git a/purithm/build.gradle b/purithm/build.gradle index 9bdfca3..b425730 100644 --- a/purithm/build.gradle +++ b/purithm/build.gradle @@ -36,6 +36,12 @@ dependencies { implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4' implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4' implementation("software.amazon.awssdk:s3") + + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' compileOnly 'org.projectlombok:lombok' @@ -45,6 +51,26 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } +sourceSets { + main { + java { + srcDirs = ['src/main/java', 'src/main/generated'] + } + } +} + +tasks.withType(JavaCompile) { + options.annotationProcessorPath = configurations.annotationProcessor + options.generatedSourceOutputDirectory = file('src/main/generated') +} + +clean { + delete file('src/main/generated') +} + tasks.named('test') { useJUnitPlatform() } + + + diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterController.java b/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterController.java index 6eeb225..bc4765c 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterController.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterController.java @@ -6,6 +6,7 @@ import com.example.purithm.domain.filter.dto.response.FilterReviewDto; import com.example.purithm.domain.filter.dto.response.filterDetailValue.IOSFilterDetailDto; import com.example.purithm.domain.filter.entity.OS; +import com.example.purithm.domain.filter.entity.Tag; import com.example.purithm.domain.filter.service.FilterService; import com.example.purithm.global.response.SuccessResponse; @@ -25,7 +26,7 @@ public class FilterController implements FilterControllerDocs { private final FilterService filterService; @GetMapping - public SuccessResponse getFilters(Long id, OS os, String tag, String sortedBy, int page, int size + public SuccessResponse getFilters(Long id, OS os, Tag tag, String sortedBy, int page, int size ) { return SuccessResponse.of(filterService.getFilters(id, page, size, os, tag, sortedBy)); } diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterControllerDocs.java b/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterControllerDocs.java index c1c2147..5f88b3e 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterControllerDocs.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/controller/FilterControllerDocs.java @@ -6,6 +6,7 @@ import com.example.purithm.domain.filter.dto.response.FilterReviewDto; import com.example.purithm.domain.filter.dto.response.filterDetailValue.IOSFilterDetailDto; import com.example.purithm.domain.filter.entity.OS; +import com.example.purithm.domain.filter.entity.Tag; import com.example.purithm.global.auth.annotation.LoginInfo; import com.example.purithm.global.response.SuccessResponse; import io.swagger.v3.oas.annotations.Operation; @@ -22,7 +23,7 @@ public SuccessResponse getFilters( @LoginInfo Long id, @RequestParam(value = "os", required = true) @Parameter(description = "휴대폰 os", examples = {@ExampleObject(value = "AOS"), @ExampleObject(value = "iOS")}) OS os, - @RequestParam(value = "tag", required = false) String tag, + @RequestParam(value = "tag", required = false) Tag tag, @RequestParam(value = "sortedBy", required = false) @Parameter(description = "정렬순", examples = {@ExampleObject(name = "최신순", summary = "최신순 정렬", value = "latest"), diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/entity/Filter.java b/purithm/src/main/java/com/example/purithm/domain/filter/entity/Filter.java index fe00cb7..fc78ce6 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/entity/Filter.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/entity/Filter.java @@ -52,4 +52,6 @@ public class Filter { private Membership membership; private OS os; + + private Tag tag; } diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/entity/StringToTagConverter.java b/purithm/src/main/java/com/example/purithm/domain/filter/entity/StringToTagConverter.java new file mode 100644 index 0000000..d914360 --- /dev/null +++ b/purithm/src/main/java/com/example/purithm/domain/filter/entity/StringToTagConverter.java @@ -0,0 +1,13 @@ +package com.example.purithm.domain.filter.entity; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + + +@Component +public class StringToTagConverter implements Converter { + @Override + public Tag convert(String source) { + return Tag.fromValue(source); + } +} \ No newline at end of file diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/entity/Tag.java b/purithm/src/main/java/com/example/purithm/domain/filter/entity/Tag.java index cf6409e..fe22851 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/entity/Tag.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/entity/Tag.java @@ -1,21 +1,26 @@ package com.example.purithm.domain.filter.entity; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; -@Entity -public class Tag { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +@AllArgsConstructor +public enum Tag { + SPRING("봄"), + SUMMER("여름"), + FALL("가을"), + WINTER("겨울"), + BACKLIGHT("역광에서"), + NIGHT("night"), + DAILY("daily"); - @ManyToOne - @JoinColumn(name = "filter_id") - private Filter filter; + private final String value; - private String tag; + public static Tag fromValue(String value) { + for (Tag tag : Tag.values()) { + if (tag.value.equals(value)) { + return tag; + } + } + + throw new IllegalArgumentException("Unknown Tag value: " + value); + } } diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepository.java b/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepository.java new file mode 100644 index 0000000..c84f3d6 --- /dev/null +++ b/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepository.java @@ -0,0 +1,14 @@ +package com.example.purithm.domain.filter.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import com.example.purithm.domain.filter.entity.Filter; +import com.example.purithm.domain.filter.entity.OS; +import com.example.purithm.domain.filter.entity.Tag; + +public interface CustomFilterRepository { + Page findAllByOs(OS os, Tag tag, Pageable pageable); + Page findAllWithLikeSorting(OS os, Tag tag, Pageable pageable); + Page findAllWithReviewSorting(OS os, Tag tag, Pageable pageable); +} diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepositoryImpl.java b/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepositoryImpl.java new file mode 100644 index 0000000..4a12b31 --- /dev/null +++ b/purithm/src/main/java/com/example/purithm/domain/filter/repository/CustomFilterRepositoryImpl.java @@ -0,0 +1,119 @@ +package com.example.purithm.domain.filter.repository; + +import static com.example.purithm.domain.filter.entity.QFilter.*; +import static com.example.purithm.domain.filter.entity.QFilterLike.*; +import static com.example.purithm.domain.review.entity.QReview.*; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import com.example.purithm.domain.filter.entity.Filter; +import com.example.purithm.domain.filter.entity.OS; +import com.example.purithm.domain.filter.entity.Tag; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.Tuple; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class CustomFilterRepositoryImpl implements CustomFilterRepository { + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Page findAllByOs(OS os, Tag tag, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder(); + builder.and(filter.os.eq(os)); + if (tag != null) { + builder.and(filter.tag.eq(tag)); + } + + List results = jpaQueryFactory + .selectFrom(filter) + .where(builder) + .orderBy(filter.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = jpaQueryFactory + .select(filter.count()) + .from(filter) + .where(filter.os.eq(os)).fetchOne(); + + return new PageImpl<>(results, pageable, total); + } + + @Override + public Page findAllWithLikeSorting(OS os, Tag tag, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder(); + builder.and(filter.os.eq(os)); + if (tag != null) { + builder.and(filter.tag.eq(tag)); + } + + List tuples = jpaQueryFactory + .select(filter, filterLike.count()) + .from(filter) + .leftJoin(filterLike).on(filter.id.eq(filterLike.filter.id)) + .where(builder) + .groupBy(filter.id) + .orderBy(filterLike.count().desc(), filter.id.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + long total = jpaQueryFactory + .select(filter.count()) + .from(filter) + .leftJoin(filterLike).on(filter.id.eq(filterLike.filter.id)) + .where(builder) + .fetchOne(); + + List results = tuples.stream() + .map(tuple -> new Object[]{tuple.get(filter), tuple.get(filterLike.count())}) + .collect(Collectors.toList()); + + return new PageImpl<>(results, pageable, total); + } + + @Override + public Page findAllWithReviewSorting(OS os, Tag tag, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder(); + builder.and(filter.os.eq(os)); + if (tag != null) { + builder.and(filter.tag.eq(tag)); + } + + List tuples = jpaQueryFactory + .select(filter, review.pureDegree.avg().as("avg")) + .from(filter) + .leftJoin(review).on(filter.id.eq(review.filter.id)) + .where(builder) + .groupBy(filter.id) + .orderBy(review.pureDegree.avg().desc(), filter.id.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + long total = jpaQueryFactory + .select(filter.count()) + .from(filter) + .leftJoin(review).on(filter.id.eq(review.filter.id)) + .where(builder) + .fetchOne(); + + List results = tuples.stream() + .map(tuple -> new Object[]{tuple.get(filter), tuple.get(Expressions.stringPath("avg"))}) + .collect(Collectors.toList()); + + return new PageImpl<>(results, pageable, total); + } +} diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/repository/FilterRepository.java b/purithm/src/main/java/com/example/purithm/domain/filter/repository/FilterRepository.java index 4e20327..1894c69 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/repository/FilterRepository.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/repository/FilterRepository.java @@ -1,32 +1,10 @@ package com.example.purithm.domain.filter.repository; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import com.example.purithm.domain.filter.entity.Filter; -import com.example.purithm.domain.filter.entity.OS; @Repository -public interface FilterRepository extends JpaRepository { - @Query("SELECT f FROM Filter f WHERE f.os=:os ORDER BY f.createdAt DESC") - Page findAllByOs(OS os, Pageable pageable); - @Query("SELECT f, COUNT(fl.id) AS like_count " + - "FROM Filter f " + - "LEFT JOIN FilterLike fl ON f.id = fl.filter.id " + - "WHERE f.os = :os " + - "GROUP BY f.id " + - "ORDER BY like_count DESC, f.id ASC") - Page findAllWithLikeSorting(OS os, Pageable pageable); - - @Query("SELECT f, AVG(r.pureDegree) AS avg " + - "FROM Filter f " + - "LEFT JOIN Review r ON f.id = r.filter.id " + - "WHERE f.os=:os " + - "GROUP BY f.id " + - "ORDER BY avg DESC, f.id ASC") - Page findAllWithReviewSorting(OS os, Pageable pageable); - +public interface FilterRepository extends JpaRepository, CustomFilterRepository { } diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/repository/QuerydslConfig.java b/purithm/src/main/java/com/example/purithm/domain/filter/repository/QuerydslConfig.java new file mode 100644 index 0000000..070d534 --- /dev/null +++ b/purithm/src/main/java/com/example/purithm/domain/filter/repository/QuerydslConfig.java @@ -0,0 +1,21 @@ +package com.example.purithm.domain.filter.repository; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@Configuration +public class QuerydslConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/repository/TagRepository.java b/purithm/src/main/java/com/example/purithm/domain/filter/repository/TagRepository.java deleted file mode 100644 index 7b0c97e..0000000 --- a/purithm/src/main/java/com/example/purithm/domain/filter/repository/TagRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.purithm.domain.filter.repository; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.example.purithm.domain.filter.entity.OS; -import com.example.purithm.domain.filter.entity.Tag; - -@Repository -public interface TagRepository extends JpaRepository { - @Query("SELECT t.filter FROM Tag t WHERE t.tag = :tag AND t.filter.os = :os") - Page findFilterByTagAndOs(@Param("tag") String tag, @Param("os") OS os, Pageable pageable); -} diff --git a/purithm/src/main/java/com/example/purithm/domain/filter/service/FilterService.java b/purithm/src/main/java/com/example/purithm/domain/filter/service/FilterService.java index 26fb39a..e822e06 100644 --- a/purithm/src/main/java/com/example/purithm/domain/filter/service/FilterService.java +++ b/purithm/src/main/java/com/example/purithm/domain/filter/service/FilterService.java @@ -23,11 +23,11 @@ import com.example.purithm.domain.filter.entity.FilterLike; import com.example.purithm.domain.filter.entity.IOSFilterDetail; import com.example.purithm.domain.filter.entity.OS; +import com.example.purithm.domain.filter.entity.Tag; import com.example.purithm.domain.filter.repository.AOSFilterDetailRepository; import com.example.purithm.domain.filter.repository.FilterLikeRepository; import com.example.purithm.domain.filter.repository.IOSFilterDetailRepository; import com.example.purithm.domain.filter.repository.FilterRepository; -import com.example.purithm.domain.filter.repository.TagRepository; import com.example.purithm.domain.review.repository.ReviewRepository; import com.example.purithm.domain.user.entity.User; import com.example.purithm.domain.user.repository.UserRepository; @@ -43,7 +43,6 @@ public class FilterService { private final FilterRepository filterRepository; - private final TagRepository tagRepository; private final IOSFilterDetailRepository iOSFilterDetailRepository; private final AOSFilterDetailRepository aOSFilterDetailRepository; private final UserRepository userRepository; @@ -51,7 +50,7 @@ public class FilterService { private final ReviewRepository reviewRepository; - public FilterListDto getFilters(Long id, int page, int size, OS os, String tag, String sortedBy) { + public FilterListDto getFilters(Long id, int page, int size, OS os, Tag tag, String sortedBy) { User user = userRepository.findById(id) .orElseThrow(() -> CustomException.of(Error.NOT_FOUND_ERROR)); @@ -59,52 +58,40 @@ public FilterListDto getFilters(Long id, int page, int size, OS os, String tag, Page filters; boolean isLast; List filterDtos; - if (tag == null) { - if (sortedBy.equals("popular")) { - filters = filterRepository.findAllWithLikeSorting(os, pageRequest); - isLast = filters.isLast(); - filterDtos = filters.getContent().stream().map(filter -> - FilterDto.of( - (Filter) filter[0], - user.getMembership(), - isLike(((Filter) filter[0]).getId(), id), - filterLikeRepository.getLikes((Filter) filter[0]))).toList(); - } else if (sortedBy.equals("pure")) { - filters = filterRepository.findAllWithReviewSorting(os, pageRequest); - isLast = filters.isLast(); - filterDtos = filters.getContent().stream().map(filter -> - FilterDto.of( - (Filter) filter[0], - user.getMembership(), - isLike(((Filter) filter[0]).getId(), id), - filterLikeRepository.getLikes((Filter) filter[0]))).toList(); - } else { - pageRequest = PageRequest.of(page, size, Sort.by("createdAt").descending()); // 정렬 없을 때는 최신 순 - Page filterByLatest = filterRepository.findAllByOs(os, pageRequest); - isLast = filterByLatest.isLast(); - filterDtos = filterByLatest.getContent().stream().map(filter -> - FilterDto.of( - filter, - user.getMembership(), - isLike((filter).getId(), id), - filterLikeRepository.getLikes(filter))).toList(); - } - - return FilterListDto.builder() - .isLast(isLast) - .filters(filterDtos).build(); + + if (sortedBy.equals("popular")) { + filters = filterRepository.findAllWithLikeSorting(os, tag, pageRequest); + isLast = filters.isLast(); + filterDtos = filters.getContent().stream().map(filter -> + FilterDto.of( + (Filter) filter[0], + user.getMembership(), + isLike(((Filter) filter[0]).getId(), id), + filterLikeRepository.getLikes((Filter) filter[0]))).toList(); + } else if (sortedBy.equals("pure")) { + filters = filterRepository.findAllWithReviewSorting(os, tag, pageRequest); + isLast = filters.isLast(); + filterDtos = filters.getContent().stream().map(filter -> + FilterDto.of( + (Filter) filter[0], + user.getMembership(), + isLike(((Filter) filter[0]).getId(), id), + filterLikeRepository.getLikes((Filter) filter[0]))).toList(); + } else { + pageRequest = PageRequest.of(page, size, Sort.by("createdAt").descending()); // 정렬 없을 때는 최신 순 + Page filterByLatest = filterRepository.findAllByOs(os, tag, pageRequest); + isLast = filterByLatest.isLast(); + filterDtos = filterByLatest.getContent().stream().map(filter -> + FilterDto.of( + filter, + user.getMembership(), + isLike((filter).getId(), id), + filterLikeRepository.getLikes(filter))).toList(); } - filters = tagRepository.findFilterByTagAndOs(tag, os, pageRequest); return FilterListDto.builder() - .isLast(filters.isLast()) - .filters( - filters.getContent().stream().map(filter -> - FilterDto.of( - (Filter) filter[0], - user.getMembership(), - isLike(((Filter) filter[0]).getId(), id), - filterLikeRepository.getLikes((Filter) filter[0]))).toList()).build(); + .isLast(isLast) + .filters(filterDtos).build(); } private boolean isLike(Long filterId, Long userId) { diff --git a/purithm/src/main/java/com/example/purithm/domain/review/entity/Review.java b/purithm/src/main/java/com/example/purithm/domain/review/entity/Review.java index 6857169..869e7b8 100644 --- a/purithm/src/main/java/com/example/purithm/domain/review/entity/Review.java +++ b/purithm/src/main/java/com/example/purithm/domain/review/entity/Review.java @@ -47,7 +47,7 @@ public class Review { @JoinColumn(name = "filter_id", nullable = false) private Filter filter; - private String review; + private String content; @Convert(converter = StringListConverter.class) private List pictures; diff --git a/purithm/src/main/java/com/example/purithm/domain/review/service/ReviewService.java b/purithm/src/main/java/com/example/purithm/domain/review/service/ReviewService.java index 03d5056..9ac8b28 100644 --- a/purithm/src/main/java/com/example/purithm/domain/review/service/ReviewService.java +++ b/purithm/src/main/java/com/example/purithm/domain/review/service/ReviewService.java @@ -29,7 +29,7 @@ public ReviewResponseDto getReview(Long id) { .orElseThrow(() -> CustomException.of(Error.NOT_FOUND_ERROR)); return ReviewResponseDto.builder() - .content(review.getReview()) + .content(review.getContent()) .username(review.getUser().getUsername()) .userProfile(review.getUser().getProfile()) .createdAt(review.getCreatedAt()) @@ -47,7 +47,7 @@ public CreatedReviewDto writeReview(Long id, ReviewRequestDto request) { Review review = Review.builder() .user(user) .filter(filter) - .review(request.content()) + .content(request.content()) .pictures(request.picture()) .pureDegree(request.pureDegree()) .build();