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

[주문] 이서연 미션 제출합니다. #8

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

sycuuui
Copy link

@sycuuui sycuuui commented Nov 20, 2024

주문 미션 제출합니다.

3주차 피드백의 객체는 객체답게 사용한다 파드백 중심으로 한 객체에서 처리할 수 있는 메소드들은 처리할 수 있도록 노력하였습니다.
orders객체에서 service계층의 행동을 한 것 같아 어떻게 분리해야될지 고민하다가 service 없이 진행하였는데 이 부분 중심으로 리뷰 주시면 감사하겠습니다!

Comment on lines +18 to +29
public String[] requestOrder() {
System.out.println(InputMessage.ORDER_MESSAGE.getMessage());
String input = scanner.nextLine();
String[] orders = input.split(",");

if (!inputValidator.requestOrderValidator(orders)) {
System.out.println(ErrorMessage.ORDER.getMessage());
throw new IllegalArgumentException(ErrorMessage.ORDER.getMessage());
}

return orders;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우테코 요구사항 중에 배열 사용을 제한하고 컬렉션 사용을 권장하는 내용이 있었습니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우테코 요구사항 중에 배열 사용을 제한하고 컬렉션 사용을 권장하는 내용이 있었습니다!

우테코 공통 피드백에서 언급된 내용이군요! 해당 문서에서 배열 대신 컬렉션을 사용하자고 주장한 근거는 다양한 API를 사용할 수 있다는 것이었어요. 그럼, 그런 API가 필요하지 않은 경우에도 컬렉션을 사용하는 것이 더 좋을까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

당장은 사용안할 수 있지만 언제든지 사용할 수도 있는 것으로 변경될 수 있는 부분이라고 생각합니다
다른 사람들과 협업한다고 가정했을 때 컬렉션을 예상했는데 메서드의 반환값이 배열이라면 API 사용이 어려워질 것이고 List로 변경하는 로직을 만들어야 할지도 모릅니다
또, 여러명이 이러한 상황에 직면하게 된다면 불필요한 코드가 반복될 수 있습니다

Comment on lines +20 to +24
/**
* 메뉴 그룹 체크 메소드
*
* @return true-음료만으로 주문하지 않을 경우
*/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설명이 필요한 부분 주석해주신 것 좋습니다😊

Comment on lines +11 to +13
if (checkEmptyBySplit(orderMenu)) {
return false;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앞에서 format에 맞지 않는다면 false를 반환하게 설계했는데 empty 여부를 판단하는 이 조건문이 필요할까요?

Comment on lines +23 to +26
if (!inputValidator.requestOrderValidator(orders)) {
System.out.println(ErrorMessage.ORDER.getMessage());
throw new IllegalArgumentException(ErrorMessage.ORDER.getMessage());
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation 클래스에서 boolean 타입을 반환하고 예외 던지는 코드를 본 메서드에서 다루셨는데 이유가 있으신가요?
validation 클래스 자체에서 예외를 던지는게 더 역할도 분리되고 상황별로 예외 상황을 나눌 수 있을 것 같아 좋아보이거든요

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 같은 생각입니다!

List<OrderMenuRes> orderResults = new ArrayList<>();

orders.forEach(
order -> orderResults.add(new OrderMenuRes(order.getMenu().getName(), order.getQuantity(), order.getMenu().getPrice() * order.getQuantity()))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우테코에서 정해준 줄 길이를 넘는 것 같습니다
띄어쓰기를 해주는게 좋아보입니다!

Comment on lines +22 to +29
private Menu findMenu(String name) {
Menu findmenu = Menu.findByName(name);
if (findmenu == null) {
throw new IllegalArgumentException();
}

return findmenu;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외 상황에 대한 메세지가 있었으면 좋겠습니다
예외가 던져졌을 때 어떤 예외인지 모르면 사용자나 개발자가 찾느라 어려움을 겪을 수 있을 것 같거든요

Comment on lines +6 to +9
ORDER_DETAILS_HEADER("\n[주문 내역]"),
SERVICE_HEADER("\n[서비스]"),
TOTAL_PRICE_HEADER("\n[최종 결제 금액]"),
SERVICE("서비스 만두");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 문자가 여기 존재한다면 출력문을 고치고 싶을 때 어려움을 겪을 수도 있을 것 같아요

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. 저는 Menu enum에 MenuGroup을 넣어줬었는데 이런 방법이 있었군요
이렇게 한다면 MenuGroup에 어떤 메뉴를 추가, 삭제할 때 실수가 일어날 여지가 줄어들 것 같네요
역할 분담의 측면에서도 좋은 것 같습니다👍

Comment on lines +7 to +9
MAIN(Arrays.asList(Menu.PIZZA, Menu.HAMBURGER, Menu.CHICKEN)),
SIDE(Arrays.asList(Menu.FRENCHFRIES, Menu.CHEESESTICK, Menu.SALAD)),
BEVERAGE(Arrays.asList(Menu.COLA, Menu.ZELO_COLA, Menu.ORANGE_JUICE));
Copy link

@changbill changbill Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static import를 해준다면 가독성 측면에서 더 좋을 것 같습니다!

import java.util.List;

public enum MenuGroup {
MAIN(Arrays.asList(Menu.PIZZA, Menu.HAMBURGER, Menu.CHICKEN)),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리스트를 이런 식으로 만들 때 List.of() 메서드를 추천드립니다
해당 리스트를 불변 객체로 만들어 수정할 여지를 없앨 수 있습니다

void 음료_그룹인지_잘_판단하는지_테스트() {
boolean result = MenuGroup.isBeverage(Menu.ORANGE_JUICE);

assertThat(result).isEqualTo(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

취향인 것 같지만 assertEqual(T, T) 도 한번 써보시길 추천드리고 싶습니다!
은근히 코드 길이를 줄여줘서요😊

Copy link

@taek2222 taek2222 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한 주 고생하셨습니다. 코드를 살펴보았을 때, Enum 을 사용하실 때 필드 선언 방법이 전부다 다릅니다! 또한, 주석 사용으로 가독성 향상에 힘쓰셨습니다. 하지만, 로직으로도 충분히 알 수 있는 부분은 줄이는 것도 중요합니다!

private Input input;
private Orders orders;

public OrderController() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컨트롤러에서 직접 객체를 생성한 이유가 궁금합니다.

return menu;
}
}
return null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null 처리 보다는 여기서 알맞는 메뉴가 없다는 에러를 발생시키는 건 어떨까요!?

또한, Stream 활용하면 간단한 로직을 만들 수 있어요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로그래밍에서 무엇이 없다 라는 표현을 할 때 가능한 방법이 여러가지가 있어요.

  1. null 사용
  2. 예외 투척
  3. 특별한 의미를 가진 매직값 사용(여기선 Menu enum에 INVALID 같은걸 추가하는 거겠네요.)

각 방법이 어떤 특징을 가지는지 학습해보면 좋을 것 같아요.

ONLY_BEVERAGE("음료만으로는 주문할 수 없습니다."),
MIN_ORDER_PRICE("최소 주문 금액을 만족하지 못했습니다.");

String message;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

접근 제어자를 작성하지 않은 이유가 궁금합니다!

this.message = message;
}

public String getMessage() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

약간 추천 드리는 방향인데, 클래스 이름에서 InputMessage 이면 충분히 메시지라는 의미가 담겨 있습니다. 그래서 getMessage() 를 생략해 get() 은 어떤가요!?

ORDER_DETAILS_HEADER("\n[주문 내역]"),
SERVICE_HEADER("\n[서비스]"),
TOTAL_PRICE_HEADER("\n[최종 결제 금액]"),
SERVICE("서비스 만두");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스를 서비스 만두 라고 표시하면 추후에 메뉴가 변경되었을 때, 변동하기에 번거로움이 있을 거 같아요. 이를 메뉴로 구분해 보는 건 어떨까요?

}

public void saveOrder(String[] datas) {
Pattern pattern = Pattern.compile("([가-힣\\s]+)\\((\\d+)개\\)");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

패턴도 상수로 분리하면 좋아요!

Comment on lines +18 to +26
for (String data : datas) {
Matcher matcher = pattern.matcher(data);

if (matcher.matches()) {
String menu = matcher.group(1);
int quantity = Integer.parseInt(matcher.group(2));

orders.addOrder(new Order(menu, quantity));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인덴트가 증가하는 부분이 있습니다. 이를 분리해 적용하는 건 어떨까요!?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수 분리의 기준이 궁금합니다!

Comment on lines +17 to +19
@DisplayName("메인 그룹인지 잘 판단하는지 테스트")
@Test
void 메인_그룹인지_잘_판단하는지_테스트() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 메소드와 @DisplayName 를 한 번에 사용하셨는데, 그 이유가 궁금합니다. 하나만 작성해도 테스트 결과에는 한국어로 설명됩니다.

String[] orders = order.split(",");

boolean result = inputValidator.requestOrderValidator(orders);
assertThat(result).isEqualTo(false);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 코드의 가독성을 향상시킬 수 있어요!

assertThat(result).isEqualTo(false); 해당 코드를

assertThat(result)
    .isEqualTo(false);

이런식으로요! 지금은 간단해서 괜찮지만 길어지면 유용합니다!

Copy link

@robinjoon robinjoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 서연님! 데벨업 팀의 백엔드 개발자 로빈입니다!

저희 서비스를 잘 이용해주시고 있어서 감사한 마음에 코드 리뷰를 진행했습니다! 궁금하거나 이야기 해보고 싶은 부분이 있다면 @robinjoon으로 맨션해주시면 될 것 같아요.

리뷰 코멘트 중 일부는 우테코 프리코스에서 제안한 것들과 조금 충돌이 되는 부분도 있을 수 있어요. 우테코 최종 코테를 대비하는 관점에서 프리코스에서 제안한 것들과 다른 내용은 무시해도 좋을 것 같아요. 다만, 우테코가 제안한 것이 언제나 정답은 아니기 때문에 그런 코멘트도 몇가지 남겨봤습니다.

전반적으로 잘 작성해 주신 것 같아요! 👍 더욱 깔끔한 코드를 작성하기 위해 고려해보면 좋을 법한 부분들을 제안드려보려고 합니다! 사실 우테코에서 모두 찐하게 경험할 내용이긴 한데, 미리 공부한다고 나쁠 건 없으니까요~

필드에 final을 붙일 수 있다면 모두 붙여보기

JPA의 엔티티 처럼 특별한 경우를 제외하면, 저는 아예 모든 필드에 final 키워드를 붙이고 정 안되면 그때 fianl을 삭제하는 것을 선호해요. 필드가 변경이 가능하다는 것은, 객체에 내부 상태가 있다는 것을 의미하고, 그럼 그 상태를 객체가 제대로 관리하도록 신경을 써야 한다는 것을 의미하기 때문이에요.

Stream API, 람다식, 메서드 참조 적극적으로 활용해보기

Stream API의 선언형 방식은 한번 익숙해지면 정말 편하고 읽기 좋은 코드를 만드는데 도움이 되요. 대표적으로 다음과 같은 상황에서 특히 편한 것 같아요.

  1. 컬렉션이나 배열의 원소를 다른 형태로 변형할 때.
  2. 컬렉션이나 배열의 원소 중 일부를 필터링하고 다른 형태로 변형할 때.
  3. 컬렉션이나 배열을 다른 형태로 변형할 때.

다양한 예외 처리 방법을 고려해보기

이 부분은 서연님의 깃허브 레포를 보니 자바 스프링 경험, 기타 프로그래밍 경험이 있는 것 같아서 말씀드려봐요. 어느정도 예외 처리 경험이 있어야 이해할 수 있는 부분이라서요. 만약 관련 내용 학습하시다가 막연한 내용이라 느껴진다면, 그건 경험을 더 쌓아야 된다는 신호라고 생각이 드네요.

여기서 말하는 예외는 자바의 Exception을 말하는 것이 아니라, 일반적이지 않은 모든 상황을 의미해요.

예를 들어 다음과 같은 방법들이 있을 것 같아요.

  1. null
  2. Optional
  3. 특별한 의미를 가진 객체의 상태 추가
  4. Exception
  • Checked vs UnChecked
  • 표준 예외 vs 커스텀 예외
  • Enum을 활용한 커스텀 예외 vs 클래스레벨로 구분한 커스텀 예외
  1. 기타 등등
    이런 방법들이 뭐고, 언제 쓰는게 좋을 지 공부해보면 좋을 것 같아요.

Comment on lines +4 to +12
PIZZA("피자", 25000),
HAMBURGER("햄버거", 10000),
CHICKEN("치킨", 23000),
FRENCHFRIES("감자튀김", 5000),
CHEESESTICK("치즈스틱", 7000),
SALAD("샐러드", 8000),
COLA("콜라", 2000),
ZELO_COLA("제로 콜라", 2500),
ORANGE_JUICE("오렌지 주스", 3000);
Copy link

@robinjoon robinjoon Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZELO라고 쓰셨군요! 😅

변수 이름을 보니 갑자기 생각난 주제가 있어서 던져보려 합니다.

프로그래밍에서 이름을 지을 때 이름을 어디서 가져오는지는 크게 2가지가 있어요.

  1. 도메인 영역에서
  2. 프로그래밍 혹은 컴퓨터과학 공학 영역의 용어

당연히 두 방식에는 모두 장단점이 있어요. 어떤게 있을까요?

return menu;
}
}
return null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로그래밍에서 무엇이 없다 라는 표현을 할 때 가능한 방법이 여러가지가 있어요.

  1. null 사용
  2. 예외 투척
  3. 특별한 의미를 가진 매직값 사용(여기선 Menu enum에 INVALID 같은걸 추가하는 거겠네요.)

각 방법이 어떤 특징을 가지는지 학습해보면 좋을 것 같아요.

ONLY_BEVERAGE("음료만으로는 주문할 수 없습니다."),
MIN_ORDER_PRICE("최소 주문 금액을 만족하지 못했습니다.");

String message;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enum에는 final 필드가 아닌 필드가 있는 것이 어색하네요. IDE에서도 아래와 같이 주의 표시를 해주네요. 이유가 궁금하다면 테코톡을 참고해보면 좋을 것 같아요!
image

}

public String serviceMessage(int quantity) {
return SERVICE.message + "(" + quantity + "개)";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String.format() 메서드를 사용하면 더 가독성 높은 코드가 될 것 같아요.

Comment on lines +25 to +32
public boolean checkMenuGroup() {
for (Order order : orders) {
if (!MenuGroup.isBeverage(order.getMenu())) {
return true;
}
}
return false;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 이름을 isNotBeverageOnly로 지으면 주석이 없어도 읽기 좋은 코드가 될 것 같네요~

Comment on lines +12 to +15
@BeforeEach
void setup() {
inputValidator = new InputValidator();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부에 상태가 없는 클래스인데, 매 테스트 메서드마다 객체를 새로 생성할 필요가 있을까요?

Comment on lines +18 to +29
public String[] requestOrder() {
System.out.println(InputMessage.ORDER_MESSAGE.getMessage());
String input = scanner.nextLine();
String[] orders = input.split(",");

if (!inputValidator.requestOrderValidator(orders)) {
System.out.println(ErrorMessage.ORDER.getMessage());
throw new IllegalArgumentException(ErrorMessage.ORDER.getMessage());
}

return orders;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우테코 요구사항 중에 배열 사용을 제한하고 컬렉션 사용을 권장하는 내용이 있었습니다!

우테코 공통 피드백에서 언급된 내용이군요! 해당 문서에서 배열 대신 컬렉션을 사용하자고 주장한 근거는 다양한 API를 사용할 수 있다는 것이었어요. 그럼, 그런 API가 필요하지 않은 경우에도 컬렉션을 사용하는 것이 더 좋을까요?


public int deliverPrice() {
int totalOrderPrice = totalOrderPrice();
if (totalOrderPrice < 50000) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

매직 넘버를 상수로 분리해 적용하는 건 어떨까요!?

상수로 분리하는 것은 어떤 단점이 있을까요? 장점만 가지고 있을까요?

import order.view.Input;
import order.view.Output;

public class OrderController {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller는 MVC 패턴에서 온 개념을 차용한 건가요? 만약 그렇다면 MVC 패턴이 이 미션의 구현에 적합한 패턴일까요? 또, 이 클래스는 Controller 라는 이름을 붙일만한 클래스일까요?

}

public void saveOrder(String[] datas) {
Pattern pattern = Pattern.compile("([가-힣\\s]+)\\((\\d+)개\\)");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자의 입력을 검증하는 역할은 InputValidator가 수행하는 것이 더 나을 것 같아요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동의합니다!

Copy link

@likerhythm likerhythm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미션 수고하셨습니다!
좋은 리뷰가 이미 많이 있네요. 그래도 최대한 리뷰해보았으니 참고해주세요!
다음 미션도 화이팅입니다 💪

Comment on lines +23 to +26
if (!inputValidator.requestOrderValidator(orders)) {
System.out.println(ErrorMessage.ORDER.getMessage());
throw new IllegalArgumentException(ErrorMessage.ORDER.getMessage());
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 같은 생각입니다!

Comment on lines +17 to +20
public void orderValidator() {
checkMenuGroup();
checkMinOrderPrice();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스 이름과 메서드 이름이 겹치는건 좋지 않다고 알고 있어요!
간단하게 validate라는 이름은 어떤가요?

@@ -0,0 +1,8 @@
package order.dto;

public record OrderMenuRes(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 같은 생각입니다! Res가 Result인지 Response인지 헷갈려서 오해를 불러일으킬 수도 있을 것 같아요!

Comment on lines +17 to +23
public static boolean isBeverage(Menu menu) {
return BEVERAGE.menus.contains(menu);
}

public static boolean isMain(Menu menu) {
return MAIN.menus.contains(menu);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isBeverage, isMain은 MenuGroup보다는 Menu에 조금 더 어울린다고 느껴지네요! 개인적인 의견입니다 :)
MenuGroup에 두는 것도 의도에 따라서는 괜찮다는 생각이 드네요 ㅎㅎ

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 Menu에 더 어울린다고 느껴지네요!

Comment on lines +70 to +72
orders.forEach(
order -> orderResults.add(new OrderMenuRes(order.getMenu().getName(), order.getQuantity(), order.getMenu().getPrice() * order.getQuantity()))
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배우고 갑니다 :)

Comment on lines +5 to +7
public class Order {
private final Menu menu;
private final int quantity;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order가 하나의 메뉴에 대한 정보를 다루는 건 생각을 못했는데 좋은 것 같네요!

@@ -0,0 +1,17 @@
package order.message;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message라는 패키지로 관리하니 좀 더 깔끔하다고 느껴지네요 :)

Comment on lines +17 to +23
public static boolean isBeverage(Menu menu) {
return BEVERAGE.menus.contains(menu);
}

public static boolean isMain(Menu menu) {
return MAIN.menus.contains(menu);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 Menu에 더 어울린다고 느껴지네요!

TOTAL_PRICE_HEADER("\n[최종 결제 금액]"),
SERVICE("서비스 만두");

private String message;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 final 키워드가 있어야 할 것 같습니다!

Comment on lines +27 to +29
if (!MenuGroup.isBeverage(order.getMenu())) {
return true;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent가 우테코의 요구사항을 넘어갑니다. 해당 부분 메서드 추출하는건 어떨까요?

Comment on lines +70 to +72
orders.forEach(
order -> orderResults.add(new OrderMenuRes(order.getMenu().getName(), order.getQuantity(), order.getMenu().getPrice() * order.getQuantity()))
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 자료 감사합니다!

}

public void saveOrder(String[] datas) {
Pattern pattern = Pattern.compile("([가-힣\\s]+)\\((\\d+)개\\)");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동의합니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants