Skip to content

Commit

Permalink
errCode fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sangkenlee committed Mar 11, 2024
1 parent 4bc7ecb commit d84d983
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 74 deletions.
75 changes: 24 additions & 51 deletions internal/delivery/http/policy-template.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,14 @@ func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *h
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_ORGANIZATION_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
return
}

ErrorJSON(w, r, err)
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

Expand Down Expand Up @@ -157,7 +152,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *h
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

Expand Down Expand Up @@ -204,15 +199,15 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_ORGANIZATION_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

Expand All @@ -224,7 +219,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE", ""))
return
}

Expand Down Expand Up @@ -298,19 +293,14 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter

policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_ORGANIZATION_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
return
}

ErrorJSON(w, r, err)
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

Expand All @@ -319,7 +309,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", ""))
return
}

Expand Down Expand Up @@ -406,33 +396,27 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter,
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_ORGANIZATION_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

version, ok := vars["version"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid version"), "C_INVALID_ORGANIZATION_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
return
}

ErrorJSON(w, r, err)
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

policyTemplate, err := h.usecase.GetPolicyTemplateVersion(r.Context(), id, version)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", ""))
return
}

Expand Down Expand Up @@ -464,19 +448,13 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
return
}

ErrorJSON(w, r, err)
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

Expand All @@ -492,7 +470,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite

currentVer, err := semver.NewVersion(input.CurrentVersion)
if err != nil {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid currentVersion"), "C_INVALID_CURRENT_VERSION", fmt.Sprintf("invalid version format '%s'", input.CurrentVersion)))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid currentVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("invalid version format '%s'", input.CurrentVersion)))
return
}

Expand All @@ -513,7 +491,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite
}

if expectedVersion != input.ExpectedVersion {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid expectedVersion"), "C_INVALID_EXPECTED_VERSION", fmt.Sprintf("expected version mismatch '%s' != '%s'",
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid expectedVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("expected version mismatch '%s' != '%s'",
input.ExpectedVersion, expectedVersion)))
}

Expand Down Expand Up @@ -546,25 +524,20 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite
vars := mux.Vars(r)
policyTemplateId, ok := vars["policyTemplateId"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", ""))
return
}

version, ok := vars["version"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid version"), "C_INVALID_VERSION", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", ""))
return
}

id, err := uuid.Parse(policyTemplateId)
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
return
}

ErrorJSON(w, r, err)
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_INVALID_POLICY_TEMPLATE_VERSION", ""))
return
}

Expand All @@ -573,7 +546,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite
if err != nil {
log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err)
if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound {
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", ""))
return
}

Expand All @@ -600,7 +573,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter,
policyTemplateName, ok := vars["policyTemplateName"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateName not found in path"),
"C_INVALID_POLICYTEMPLATE_NAME", ""))
"PT_INVALID_POLICY_TEMPLATE_NAME", ""))
return
}

Expand Down Expand Up @@ -632,7 +605,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateKind(w http.ResponseWriter,
policyTemplateKind, ok := vars["policyTemplateKind"]
if !ok {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateKind not found in path"),
"C_INVALID_POLICY_TEMPLATE_KIND", ""))
"PT_INVALID_POLICY_TEMPLATE_KIND", ""))
return
}

Expand Down Expand Up @@ -668,7 +641,7 @@ func (h *PolicyTemplateHandler) RegoCompile(w http.ResponseWriter, r *http.Reque
if ok {
parsedBool, err := strconv.ParseBool(parse)
if err != nil {
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid parseParameter: '%s'", parse), "U_INVALID_REGO_PARSEPARAM", ""))
ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid parseParameter: '%s'", parse), "PT_INVALID_REGO_PARSEPARAMETER", ""))
return
}
parseParameter = parsedBool
Expand Down
18 changes: 9 additions & 9 deletions internal/usecase/policy-template.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,23 @@ func NewPolicyTemplateUsecase(r repository.Repository) IPolicyTemplateUsecase {
func (u *PolicyTemplateUsecase) Create(ctx context.Context, dto domain.PolicyTemplate) (policyTemplateId string, err error) {
user, ok := request.UserFrom(ctx)
if !ok {
return "", httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "P_INVALID_TOKEN", "")
return "", httpErrors.NewUnauthorizedError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "")
}

exists, err := u.repo.ExistByName(dto.TemplateName)
if err == nil && exists {
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_TEMPLATE_NAME", "policy template name already exists")
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_NAME", "policy template name already exists")
}

exists, err = u.repo.ExistByKind(dto.Kind)
if err == nil && exists {
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_TEMPLATE_KIND", "policy template kind already exists")
return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_KIND", "policy template kind already exists")
}

for _, organizationId := range dto.PermittedOrganizationIds {
_, err = u.organizationRepo.Get(organizationId)
if err != nil {
return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")
return "", httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")
}
}

Expand Down Expand Up @@ -119,12 +119,12 @@ func (u *PolicyTemplateUsecase) Get(ctx context.Context, policyTemplateID uuid.U
func (u *PolicyTemplateUsecase) Update(ctx context.Context, policyTemplateId uuid.UUID, update domain.UpdatePolicyTemplateRequest) (err error) {
user, ok := request.UserFrom(ctx)
if !ok {
return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "")
return httpErrors.NewBadRequestError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "")
}

_, err = u.repo.GetByID(policyTemplateId)
if err != nil {
return httpErrors.NewNotFoundError(err, "P_FAILED_FETCH_POLICY_TEMPLATE", "")
return httpErrors.NewNotFoundError(err, "PT_FAILED_FETCH_POLICY_TEMPLATE", "")
}

exists, err := u.repo.ExistByName(*update.TemplateName)
Expand All @@ -136,7 +136,7 @@ func (u *PolicyTemplateUsecase) Update(ctx context.Context, policyTemplateId uui
for _, organizationId := range *update.PermittedOrganizationIds {
_, err = u.organizationRepo.Get(organizationId)
if err != nil {
return httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")
return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")
}
}
}
Expand Down Expand Up @@ -251,8 +251,8 @@ func (u *PolicyTemplateUsecase) RegoCompile(request *domain.RegoCompileRequest,
for _, compileError := range compiler.Errors {
response.Errors = append(response.Errors, domain.RegoCompieError{
Status: 400,
Code: "P_INVALID_REGO_SYNTAX",
Message: "Invalid rego syntax",
Code: "PT_INVALID_REGO_SYNTAX",
Message: "invalid rego syntax",
Text: fmt.Sprintf("[%d:%d] %s",
compileError.Location.Row, compileError.Location.Col,
compileError.Message),
Expand Down
42 changes: 28 additions & 14 deletions pkg/httpErrors/errorCode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ type ErrorCode string

var errorMap = map[ErrorCode]string{
// Common
"C_INTERNAL_ERROR": "예상하지 못한 오류가 발생했습니다. 문제가 계속되면 관리자에게 문의해주세요.",
"C_INVALID_ACCOUNT_ID": "유효하지 않은 어카운트 아이디입니다. 어카운트 아이디를 확인하세요.",
"C_INVALID_STACK_ID": "유효하지 않은 스택 아이디입니다. 스택 아이디를 확인하세요.",
"C_INVALID_CLUSTER_ID": "유효하지 않은 클러스터 아이디입니다. 클러스터 아이디를 확인하세요.",
"C_INVALID_APPGROUP_ID": "유효하지 않은 앱그룹 아이디입니다. 앱그룹 아이디를 확인하세요.",
"C_INVALID_ORGANIZATION_ID": "유효하지 않은 조직 아이디입니다. 조직 아이디를 확인하세요.",
"C_INVALID_PROJECT_ID": "유효하지 않은 프로젝트 아이디입니다. 아이디를 확인하세요.",
"C_INVALID_CLOUD_ACCOUNT_ID": "유효하지 않은 클라우드어카운트 아이디입니다. 클라우드어카운트 아이디를 확인하세요.",
"C_INVALID_STACK_TEMPLATE_ID": "유효하지 않은 스택템플릿 아이디입니다. 스택템플릿 아이디를 확인하세요.",
"C_INVALID_ASA_ID": "유효하지 않은 앱서빙앱 아이디입니다. 앱서빙앱 아이디를 확인하세요.",
"C_INVALID_ASA_TASK_ID": "유효하지 않은 테스크 아이디입니다. 테스크 아이디를 확인하세요.",
"C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.",
"C_INVALID_AUDIT_ID": "유효하지 않은 로그 아이디입니다. 로그 아이디를 확인하세요.",
"C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.",
"C_INTERNAL_ERROR": "예상하지 못한 오류가 발생했습니다. 문제가 계속되면 관리자에게 문의해주세요.",
"C_INVALID_ACCOUNT_ID": "유효하지 않은 어카운트 아이디입니다. 어카운트 아이디를 확인하세요.",
"C_INVALID_STACK_ID": "유효하지 않은 스택 아이디입니다. 스택 아이디를 확인하세요.",
"C_INVALID_CLUSTER_ID": "유효하지 않은 클러스터 아이디입니다. 클러스터 아이디를 확인하세요.",
"C_INVALID_APPGROUP_ID": "유효하지 않은 앱그룹 아이디입니다. 앱그룹 아이디를 확인하세요.",
"C_INVALID_ORGANIZATION_ID": "유효하지 않은 조직 아이디입니다. 조직 아이디를 확인하세요.",
"C_INVALID_PROJECT_ID": "유효하지 않은 프로젝트 아이디입니다. 아이디를 확인하세요.",
"C_INVALID_CLOUD_ACCOUNT_ID": "유효하지 않은 클라우드어카운트 아이디입니다. 클라우드어카운트 아이디를 확인하세요.",
"C_INVALID_STACK_TEMPLATE_ID": "유효하지 않은 스택템플릿 아이디입니다. 스택템플릿 아이디를 확인하세요.",
"C_INVALID_ASA_ID": "유효하지 않은 앱서빙앱 아이디입니다. 앱서빙앱 아이디를 확인하세요.",
"C_INVALID_ASA_TASK_ID": "유효하지 않은 테스크 아이디입니다. 테스크 아이디를 확인하세요.",
"C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.",
"C_INVALID_AUDIT_ID": "유효하지 않은 로그 아이디입니다. 로그 아이디를 확인하세요.",
"C_INVALID_POLICY_TEMPLATE_ID": "유효하지 않은 정책 템플릿 아이디입니다. 정책 템플릿 아이디를 확인하세요.",
"C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.",

// Auth
"A_INVALID_ID": "아이디가 존재하지 않습니다.",
Expand Down Expand Up @@ -82,6 +83,19 @@ var errorMap = map[ErrorCode]string{
"ST_CREATE_ALREADY_EXISTED_NAME": "스택 템플릿에 이미 존재하는 이름입니다.",
"ST_FAILED_UPDATE_ORGANIZATION": "스택 템플릿에 조직을 설정하는데 실패했습니다.",
"ST_NOT_EXISTED_STACK_TEMPLATE": "업데이트할 스택템플릿이 존재하지 않습니다.",

// PolicyTemplate
"PT_CREATE_ALREADY_EXISTED_NAME": "정첵 템플릿에 이미 존재하는 이름입니다.",
"PT_CREATE_ALREADY_EXISTED_KIND": "정책 템플릿에 이미 존재하는 유형입니다.",
"PT_NOT_FOUND_POLICY_TEMPLATE": "정책 템플릿이 존재하지 않습니다.",
"PT_INVALID_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.",
"PT_FAILED_FETCH_POLICY_TEMPLATE": "정책 템플릿 ID에 해당하는 정책 템플릿을 가져오는데 실패했습니다.",
"PT_INVALID_REGO_SYNTAX": "Rego 문법 오류입니다.",
"PT_INVALID_POLICY_TEMPLATE_VERSION": "유효하지 않은 정책 템플릿 버전닙니다. 정책 템플릿 버전을 확인하세요.",
"PT_NOT_FOUND_POLICY_TEMPLATE_VERSION": "정책 템플릿 버전이 존재하지 않습니다.",
"PT_INVALID_POLICY_TEMPLATE_NAME": "유효하지 않은 정책 템플릿 이름입니다. 정책 템플릿 이름을 확인하세요.",
"PT_INVALID_POLICY_TEMPLATE_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.",
"PT_INVALID_REGO_PARSEPARAMETER": "유효하지 않은 Rego 파싱 설정입니다. Rego 파싱 설정을 확인하세요.",
}

func (m ErrorCode) GetText() string {
Expand Down

0 comments on commit d84d983

Please sign in to comment.