- 프론트엔드 아키텍처
- 프론트엔드의 상세 보기에서 새 작업 항목 위젯을 구현하는 단계
- 프론트엔드에서 새 작업 항목 위젯을 구현하는 단계
- 작성 보기에서 값을 저장하는 Apollo 캐시
- Apollo 캐시에서 작성 양식에 새 위젯 지원
- 위젯을 작업 항목 유형에 매핑
- 백엔드 아키텍처
- 새로운 백엔드 위젯 만들기
Work items 위젯
프론트엔드 아키텍처
작업 항목을 위한 위젯은 프론트엔드 위젯에서 큰 영감을 받았습니다. 작업 항목은 이슈와 아키텍처적으로 다르기 때문에 몇 가지 차이가 예상됩니다.
GraphQL (Vue Apollo)은 작업 항목 위젯 스택의 핵심을 이룹니다.
작업 항목을 위한 위젯 정보 검색
작업 항목을 표시하려면 프론트엔드는 표시하려는 작업 항목에서 사용 가능한 위젯을 알아야 합니다. 이를 위해 다음과 같은 쿼리를 사용하여 위젯 목록을 가져와야 합니다.
query workItem($workItemId: WorkItemID!) {
workItem(id: $workItemId) {
id
widgets {
... on WorkItemWidgetAssignees {
type
assignees {
nodes {
name
}
}
}
}
}
}
GraphQL 쿼리와 뮤테이션
GraphQL 쿼리와 뮤테이션은 작업 항목에 독립적입니다. 작업 항목 쿼리와 뮤테이션은 위젯 레벨에서 발생해야 하므로 위젯은 독립적으로 재사용 가능한 구성 요소여야 합니다. 작업 항목 쿼리와 뮤테이션은 모든 작업 항목 유형을 지원해야 하며 동적이어야 합니다. 위젯 식별자를 지정하여 어떤 작업 항목 속성도 쿼리하고 변이시켜야 합니다.
다음 쿼리 예에서 설명 위젯은 어떤 작업 항목의 설명을 표시하고 업데이트하는 데 쿼리와 뮤테이션을 사용합니다.
query workItem($fullPath: ID!, $iid: String!) {
workspace: namespace(fullPath: $fullPath) {
id
workItem(iid: $iid) {
id
iid
widgets {
... on WorkItemWidgetDescription {
description
descriptionHtml
}
}
}
}
}
뮤테이션 예시:
mutation {
workItemUpdate(input: {
id: "gid://gitlab/AnyWorkItem/499"
descriptionWidget: {
description: "새 설명"
}
}) {
errors
workItem {
description
}
}
}
위젯 책임과 구조
위젯은 제목, 설명 또는 레이블과 같은 단일 속성을 표시하고 업데이트하는 데 책임이 있습니다. 위젯은 모든 유형의 작업 항목을 지원해야 합니다. 구성 요소 재사용성을 극대화하려면 위젯은 책임을 지닌 필드 래퍼여야 합니다.
필드 컴포넌트는 일반적이고 간단한 구성 요소입니다. 입력 필드, 날짜 선택기 또는 드롭다운 목록과 같은 속성이나 작업 항목의 세부 정보를 알지 못합니다.
위젯은 작업 항목에 따라 여러 사용 사례를 지원할 수 있도록 구성할 수 있어야 합니다. 위젯을 구축할 때 속성을 제공하고 props와 주입된 속성의 사용을 최소화하면서 추가 컨텍스트를 제공하기 위해 슬롯을 사용해야합니다.
예시
현재 우리는 폴더에서 찾을 수 있는 많은 편집 가능한 위젯을 보유하고 있습니다.
또한 재사용 가능한 기본 드롭다운 위젯 래퍼를 보유하고 있으며, 이 위젯은 드롭다운을 가진 새 위젯에 사용할 수 있도록 지원합니다. 이 위젯은 다중 선택 및 단일 선택을 모두 지원합니다.
프론트엔드의 상세 보기에서 새 작업 항목 위젯을 구현하는 단계
새 위젯 작업을 시작하기 전에
- 새 위젯의 범위를 알고 있고 새 위젯을 위한 디자인이 준비되어 있는지 확인하십시오.
- 새 위젯이 백엔드에서 이미 구현되어 있고 유효한 작업 항목 유형에 의해 반환되고 있는지 확인하십시오. 멀티버전 호환성을 위해 우리는 ~백엔드와 ~프론트엔드를 별개의 마일스톤으로 가져야 합니다.
- 위젯 업데이트가
workItemUpdate
에서 지원되는지 확인하십시오. - 각 위젯이 다른 요구 사항을 가지고 있으므로 사전에 질문을 하고 PM/UX와 논의한 후 MVC를 만드는 것이 좋은 아이디어입니다.
새 위젯에 대한 작업을 시작할 때
- 입력 필드에 따라 드롭다운, 입력 텍스트 또는 기타 사용자 정의 디자인을 사용하는지 확인하고 기존 래퍼를 사용하거나 완전히 새 컴포넌트를 사용해야 합니다.
- 이상적으로 모든 새 위젯은 우선 테스트할 공간을 확보하기 위해 FF 뒤에 있어야 하지만 위젯에 대한 우선순위가 있을 경우를 제외하고는 모든 새 위젯이 FF 뒤에 있어야 합니다.
- 폴더에 새 위젯을 만들어야 합니다.
- 사이드바에서 편집 가능한 위젯인 경우 work_item_attributes_wrapper에 포함해야 합니다.
단계
새 작업 항목 위젯을 추가하는 프로세스 예시를 보려면 병합 요청 #159720를 참조하세요.
-
app/assets/javascripts/work_items/constants.js
에I18N_WORK_ITEM_ERROR_FETCHING_<widget_name>
을 정의하십시오. - 컴포넌트
app/assets/javascripts/work_items/components/work_item_<widget_name>.vue
또는ee/app/assets/javascripts/work_items/components/work_item_<widget_name>.vue
를 생성하십시오.- 컴포넌트는
workItemByIidQuery
에서 사용 가능한 props를 받지 않아야 합니다. 이슈 #461761을 참조하십시오.
- 컴포넌트는
- 컴포넌트를 보기/편집 작업 항목 화면에 추가하십시오
app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue
. - 위젯이 새 작업 항목 생성 시에 사용 가능한 경우:
- 컴포넌트를
app/assets/javascripts/work_items/components/create_work_item.vue
에 추가하십시오. -
app/assets/javascripts/work_items/graphql/typedefs.graphql
에서 로컬 입력 유형을 정의하십시오. -
app/assets/javascripts/work_items/graphql/cache_utils.js
에서 위젯을 위한 새 작업 항목 상태 GraphQL 데이터를 스텁합니다. -
app/assets/javascripts/work_items/graphql/resolvers.js
에서 GraphQL이 GraphQL 데이터를 업데이트하는 방법을 정의하십시오.- 단일 값 위젯에 대해 특별한
CLEAR_VALUE
상수가 필요합니다. 왜냐하면 값이null
인 경우에는 값이null
이지만 깨끗하게 지운 것인지, 기본값이null
인지를 구분할 수 없기 때문입니다. 이 예는ee/app/assets/javascripts/work_items/components/work_item_health_status.vue
를 참조하십시오. 대부분의 다중 값을 지원하는 대부분의 위젯에는[]
와null
을 구분할 수 있기 때문에 필요하지 않습니다.
- 단일 값 위젯에 대해 특별한
- 위젯에 대한 GraphQL 쿼리를 추가하십시오:
- CE 위젯의 경우
app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
및ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
에, - EE 위젯의 경우
ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
에 추가하십시오.
- CE 위젯의 경우
- 컴포넌트를
- 번역을 업데이트하십시오:
tooling/bin/gettext_extractor locale/gitlab.pot
.
이 시점에서 프론트엔드에서 위젯을 사용할 수 있어야 합니다.
이제 기존 파일에 대한 테스트를 업데이트하고 새 파일에 대한 테스트를 작성할 수 있습니다.
-
spec/frontend/work_items/components/create_work_item_spec.js
또는ee/spec/frontend/work_items/components/create_work_item_spec.js
. -
spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js
또는ee/spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js
. -
spec/frontend/work_items/components/work_item_<widget_name>_spec.js
또는ee/spec/frontend/work_items/components/work_item_<widget_name>_spec.js
. -
spec/frontend/work_items/graphql/resolvers_spec.js
또는ee/spec/frontend/work_items/graphql/resolvers_spec.js
. -
spec/features/projects/work_items/work_item_spec.rb
또는ee/spec/features/projects/work_items/work_item_spec.rb
.
참고:
과도한 SQL 쿼리로 인해 일부 기능 사양이 실패할 수 있습니다.
이를 해결하기 위해 spec/support/shared_examples/features/work_items/rolledup_dates_shared_examples.rb
에서 모의 Gitlab::QueryLimiting::Transaction.threshold
를 업데이트하십시오.
프론트엔드에서 새 작업 항목 위젯을 구현하는 단계
- 새 위젯에 대한 범위를 파악하고 디자인이 준비되었는지 확인하세요.
- 새 위젯이 이미 백엔드에서 구현되어 작업 항목 쿼리에 의해 반환되고 있는지 확인하고, 다중 버전 호환성으로 인해~백엔드와~프론트엔드가 별도의 마일스톤에 있는지 확인하세요.
- 위젯이
workItemCreate
뮤테이션에서 지원되는지 확인하세요. - 새로운 프론트엔드 위젯이 디자인을 기반으로 만들어진 후, 작업 항목 보기 작성에 포함되어 있는지 확인하세요.
작성 보기에서 값을 저장하는 Apollo 캐시
작성 뷰는 상세 보기와 거의 동일하기 때문에 각 위젯의 드래프트 데이터를 저장하고 싶었고, 특정 유형의 각 새로운 작업 항목에는 새로운 캐시 항목 apollo가 있습니다.
예를 들어, 작성 뷰를 초기화할 때, work items cache utils에 있는 setNewWorkItemCache
함수가 호출되며, 이는 작업 항목 모달 작성 보기 및 작업 항목 구성 요소 작성 보기 모두에서 호출됩니다.
사용에 따라 작성 보기를 어떤 Vue 파일이든 포함시킬 수 있습니다. 작성 보기의 workItemType
를 전달하면 작업 항목 유형 쿼리에서 가져온 적용 가능한 작업 항목 위젯만 포함되며, 위젯 정의에서만 보여집니다.
작성 보기에서 작성한 드래프트의 값을 가져오는 로컬 뮤테이션을 지원합니다.
Apollo 캐시에서 작성 양식에 새 위젯 지원
- 각 위젯은 별도로 사용될 수 있으므로, 각 위젯은
updateWorkItem
뮤테이션을 사용합니다. - 이제 드래프트 데이터를 업데이트하려면 데이터와 함께 캐시를 업데이트해야 합니다.
- 작업 항목을 업데이트하기 전에 새 작업 항목인지 또는 작업 항목
id
/iid
가 있는지 확인하세요. 예시.
if (this.workItemId === newWorkItemId(this.workItemType)) {
this.$apollo.mutate({
mutation: updateNewWorkItemMutation,
variables: {
input: {
workItemType: this.workItemType,
fullPath: this.fullPath,
assignees: this.localAssignees,
},
},
});
로컬 뮤테이션에서 새 작업 항목 위젯 지원
- 작업 항목 로컬 뮤테이션 유형 정보에 입력 유형을 추가하세요. 사용자 지정 객체 또는 기본값 등 무엇이든 상관없습니다.
작업 항목의 parent
에 워크 아이템의 부모의 이름과 ID가 포함된 경우 예시
input LocalParentWidgetInput {
id: String
name: String
}
input LocalUpdateNewWorkItemInput {
fullPath: String!
workItemType: String!
healthStatus: String
color: String
title: String
description: String
confidential: Boolean
parent: [LocalParentWidgetInput]
}
- 위젯에서 드래프트 저장을 지원하려면 위젯에서 새 매개변수를 전달하세요.
this.$apollo.mutate({
mutation: updateNewWorkItemMutation,
variables: {
input: {
workItemType: this.workItemType,
fullPath: this.fullPath,
parent: {
id: 'gid:://gitlab/WorkItem/1',
name: '워크 아이템의 부모'
}
},
},
})
- graphql 리졸버에서 업데이트를 지원하고 새 작업 항목 캐시를 업데이트하는 로직을 추가하세요.
const { parent } = input;
if (parent) {
const parentWidget = findWidget(WIDGET_TYPE_PARENT, draftData?.workspace?.workItem);
parentWidget.parent = parent;
const parentWidgetIndex = draftData.workspace.workItem.widgets.findIndex(
(widget) => widget.type === WIDGET_TYPE_PARENT,
);
draftData.workspace.workItem.widgets[parentWidgetIndex] = parentWidget;
}
- 작업 항목 보기 작성에서 드래프트의 값을 가져옵니다.
if (this.isWidgetSupported(WIDGET_TYPE_PARENT)) {
workItemCreateInput.parentWidget = {
id: this.workItemParentId
};
}
await this.$apollo.mutate({
mutation: createWorkItemMutation,
variables: {
input: {
...workItemCreateInput,
},
});
위젯을 작업 항목 유형에 매핑
모든 작업 항목 유형은 미리 정의된 위젯 풀을 공유하며, 특정 유형에 활성화된 위젯에 따라 사용자 정의됩니다. 사용자가 새로운 작업 항목 유형을 생성하고 그에 대한 위젯 집합을 정의 할 수 있도록 하기로 했기 때문에 각 작업 항목 유형의 위젯 매핑이 데이터베이스에 저장됩니다. 위젯 매핑은 widget_definitions 테이블에 저장되며, 기본 작업 항목 유형 및 향후 사용자 정의 유형에 대한 위젯 정의에 사용될 수 있습니다. 예상된 데이터베이스 테이블 구조에 대한 자세한 내용은 이 이슈 설명서에서 찾을 수 있습니다.
작업 항목 유형에 새 위젯 추가
각 작업 항목 유형에 할당된 위젯 정보는 데이터베이스에 저장되기 때문에, 작업 항목 유형에 새 위젯을 추가하려면 데이터베이스 마이그레이션을 통해 진행해야 합니다. 또한 위젯 가져오기(lib/gitlab/database_importers/work_items/widgets_importer.rb
)를 업데이트해야 합니다.
위젯 정의 표 구조
테이블의 각 레코드는 위젯을 작업 항목 유형에 매핑하는 것을 정의합니다. 현재 “전역” 정의만 사용됩니다(NULL namespace_id
와 함께 정의). 차후 반복에서 이러한 매핑을 사용자 정의할 수 있도록 할 계획입니다. 예를 들어 아래 테이블은 다음을 정의합니다:
- 가중치 위젯은 작업 항목 유형 0과 1에 대해 활성화되어 있습니다.
- 가중치 위젯은 작업 항목 유형 1에 대해 편집할 수 없으며 rollup 값만 포함하며, 작업 항목 유형 0은 편집 가능한 값만 포함합니다.
- 네임스페이스 1에서 가중치 위젯은 MyWeight로 이름이 변경됩니다. 사용자가 위젯의 이름을 변경하면, 이 네임스페이스의 모든 위젯 매핑의 이름을 변경하는 것이 합리적입니다 - 왜냐하면 ‘name’ 속성이 비정규화되기 때문에, 이 위젯 유형의 모든 작업 항목 유형에 대해 네임스페이스가 있는 매핑을 만들어야 합니다.
- 특정 작업 항목 유형에 대해 가중치 위젯을 비활성화할 수 있습니다 (네임스페이스 3에서는 작업 항목 유형 0에 대해 비활성화되지만, 작업 항목 유형 1에 대해서는 여전히 활성화되어 있습니다)
ID | namespace_id
| work_item_type_id
| widget_type
| widget_options
| Name | Disabled |
---|---|---|---|---|---|---|
1 | 0 | 1 | {‘editable’ => true, ‘rollup’ => false } | Weight | false | |
2 | 1 | 1 | {‘editable’ => false, ‘rollup’ => true } | Weight | false | |
3 | 1 | 0 | 1 | {‘editable’ => true, ‘rollup’ => false } | MyWeight | false |
4 | 1 | 1 | 1 | {‘editable’ => false, ‘rollup’ => true } | MyWeight | false |
5 | 2 | 0 | 1 | {‘editable’ => true, ‘rollup’ => false } | Other Weight | false |
6 | 3 | 0 | 1 | {‘editable’ => true, ‘rollup’ => false } | Weight | true |
백엔드 아키텍처
사용자 정의된 상세한 뮤테이션(예: WorkItemCreateFromTask
) 또는 workItemCreate
또는 workItemUpdate
뮤테이션의 일부로 위젯을 업데이트할 수 있습니다.
위젯 콜백
작업 항목의 뮤테이션과 함께 위젯을 업데이트할 때, 백엔드 코드는 WorkItems::Callbacks::Base
를 상속받은 콜백 클래스를 구현해야 합니다. 이러한 클래스에는 ActiveRecord 콜백과 유사한 이름의 콜백 메서드가 있으며 유사하게 작동합니다.
위젯과 동일한 이름의 콜백 클래스는 자동으로 사용됩니다. 예를 들어, 작업 항목에 AwardEmoji
위젯이있는 경우 WorkItems::Callbacks::AwardEmoji
가 호출됩니다. 다른 클래스를 사용하려면 callback_class
클래스 메서드를 오버라이드할 수 있습니다.
콜백 클래스가 병합 요청 또는 epic과 같은 다른 이슈에도 사용되는 경우, 클래스를 Issuable::Callbacks
에 정의하고 IssuableBaseService#available_callbacks
목록에 클래스를 추가하세요. 이들은 작업 항목 업데이트와 레거시 이슈, 병합 요청 또는 epic 업데이트에 대해 실행됩니다.
excluded_in_new_type?
를 사용하여 작업 항목 유형이 변경되고 위젯을 더 이상 사용할 수 없을 때를 확인하세요. 이는 일반적으로 관련이 없어진 레코드를 제거하는 트리거입니다.
사용 가능한 콜백
-
after_initialize
은 작업 항목이BuildService
에 의해 초기화된 후에 호출되며,CreateService
및UpdateService
에 의해 저장되기 전에 실행됩니다. 이 콜백은 생성 또는 업데이트 데이터베이스 트랜잭션 외부에서 실행됩니다. -
before_create
은 작업 항목이CreateService
에 의해 저장되기 전에 호출됩니다. 이 콜백은 생성 데이터베이스 트랜잭션 내에서 실행됩니다. -
before_update
은 작업 항목이UpdateService
에 의해 저장되기 전에 호출됩니다. 이 콜백은 업데이트 데이터베이스 트랜잭션 내에서 실행됩니다. -
after_create
는 작업 항목이CreateService
에 의해 저장된 후에 호출됩니다. 이 콜백은 생성 데이터베이스 트랜잭션 내에서 실행됩니다. -
after_update
는 작업 항목이UpdateService
에 의해 저장된 후에 호출됩니다. 이 콜백은 업데이트 데이터베이스 트랜잭션 내에서 실행됩니다. -
after_save
는 작업 항목이CreateService
또는UpdateService
에 의해 생성 또는 DB 업데이트 트랜잭션을 커밋하기 전에 호출됩니다. -
after_update_commit
은 작업 항목이UpdateService
에 의해 DB 업데이트 트랜잭션을 커밋한 후에 호출됩니다. -
after_save_commit
은 작업 항목이CreateService
또는UpdateService
에 의해 생성 또는 DB 업데이트 트랜잭션을 커밋한 후에 호출됩니다.
새로운 백엔드 위젯 만들기
새로운 작업 항목 위젯을 추가하는 과정에 대한 예제는 merge request #158688를 참조하세요.
- 작업 항목 뮤테이션에 위젯 인수를 추가하세요:
- 위젯이 CE 기능에 대한 것이며, 위젯이 사용 가능하고 작업 항목을 만들거나 업데이트하기 위해 동일한 인수를 가지는 경우:
app/graphql/mutations/concerns/mutations/work_items/shared_arguments.rb
. - 위젯이 하나에만 사용 가능하거나 두 뮤테이션 간에 인수가 다른 경우 EE 기능:
- 만들기:
app/graphql/mutations/concerns/mutations/work_items/create_arguments.rb
또는ee/app/graphql/ee/mutations/work_items/create.rb
. - 업데이트:
app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb
또는ee/app/graphql/ee/mutations/work_items/update.rb
.
- 만들기:
- 위젯이 CE 기능에 대한 것이며, 위젯이 사용 가능하고 작업 항목을 만들거나 업데이트하기 위해 동일한 인수를 가지는 경우:
- 위젯 인수를 정의하세요.
app/graphql/types/work_items/widgets/<widget_name>_input_type.rb
또는ee/app/graphql/types/work_items/widgets/<widget_name>_input_type.rb
에 위젯 입력 유형을 추가하세요.- 만들고 업데이트 뮤테이션 간에 입력 유형이 다른 경우,
<widget_name>_create_input_type.rb
및/또는<widget_name>_update_input_type.rb
를 사용하세요.
- 만들고 업데이트 뮤테이션 간에 입력 유형이 다른 경우,
- 위젯 필드를 정의할 것입니다.
app/graphql/types/work_items/widgets/<widget_name>_type.rb
또는ee/app/graphql/types/work_items/widgets/<widget_name>_type.rb
에 위젯 유형을 추가하세요. -
app/assets/javascripts/graphql_shared/possible_types.json
의WorkItemWidget
배열에 위젯을 추가하세요. -
app/graphql/types/work_items/widget_interface.rb
의TYPE_MAPPINGS
에 위젯 유형 매핑을 추가하세요. 또는ee/app/graphql/ee/types/work_items/widget_interface.rb
의EE_TYPE_MAPPINGS
에 추가하세요. -
app/models/work_items/widget_definition.rb
의widget_type
enum에 위젯 유형을 추가하세요. -
app/models/work_items/widgets/<widget_name>.rb
에 위젯의 일부로 사용 가능한 빠른 액션을 정의하세요. -
app/services/work_items/callbacks/<widget_name>.rb
에 뮤테이션들이 작업 항목을 생성/업데이트하는 방법을 정의하세요.-
excluded_in_new_type?
을 처리해야 하는지 여부를 고려하세요. - 오류를 처리하려면
raise_error
를 사용하세요.
-
-
lib/gitlab/database_importers/work_items/base_type_importer.rb
의WIDGET_NAMES
해시에 위젯을 정의하세요. - 해당 작업 항목 유형에 위젯을 할당하세요.
-
lib/gitlab/database_importers/work_items/base_type_importer.rb
의WIDGETS_FOR_TYPE
해시에 추가하세요. -db/migrate/<version>_add_<widget_name>_widget_to_work_item_types.rb
에 마이그레이션을 생성하세요. 최신 모범 사례에 대한 자세한 내용은 최신 discussion on merge request 148119를 참조하세요. 포스트 마이그레이션을 사용할 필요는 없습니다. - GraphQL 문서를 업데이트하세요:
bundle exec rake gitlab:graphql:compile_docs
. - 번역을 업데이트하세요:
tooling/bin/gettext_extractor locale/gitlab.pot
.
여기까지 하면 GraphQL 쿼리 및 뮤테이션을 사용할 수 있어야 합니다.
이제 기존 파일의 테스트를 업데이트하고 새 파일에 대해 테스트를 작성할 수 있습니다:
-
spec/graphql/types/work_items/widget_interface_spec.rb
또는ee/spec/graphql/types/work_items/widget_interface_spec.rb
. -
spec/models/work_items/widget_definition_spec.rb
또는ee/spec/models/ee/work_items/widget_definition_spec.rb
. -
spec/models/work_items/widgets/<widget_name>_spec.rb
또는ee/spec/models/work_items/widgets/<widget_name>_spec.rb
. - 요청:
- CE:
spec/requests/api/graphql/mutations/work_items/update_spec.rb
및/또는spec/requests/api/graphql/mutations/work_items/create_spec.rb
. - EE:
ee/spec/requests/api/graphql/mutations/work_items/update_spec.rb
및/또는ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb
.
- CE:
- Callback:
spec/services/work_items/callbacks/<widget_name>_spec.rb
또는ee/spec/services/work_items/callbacks/<widget_name>_spec.rb
. - GraphQL 유형:
spec/graphql/types/work_items/widgets/<widget_name>_type_spec.rb
또는ee/spec/graphql/types/work_items/widgets/<widget_name>_type_spec.rb
. - GraphQL 입력 유형(s):
- CE:
spec/graphql/types/work_items/widgets/<widget_name>_input_type_spec.rb
또는spec/graphql/types/work_items/widgets/<widget_name>_create_input_type_spec.rb
와spec/graphql/types/work_items/widgets/<widget_name>_update_input_type_spec.rb
. - EE:
ee/spec/graphql/types/work_items/widgets/<widget_name>_input_type_spec.rb
또는ee/spec/graphql/types/work_items/widgets/<widget_name>_create_input_type_spec.rb
와ee/spec/graphql/types/work_items/widgets/<widget_name>_update_input_type_spec.rb
.
- CE:
- 마이그레이션:
spec/migrations/<version>_add_<widget_name>_widget_to_work_item_types_spec.rb
.