summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/boards/boards_util.js21
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue5
-rw-r--r--app/assets/javascripts/boards/index.js4
-rw-r--r--app/assets/javascripts/boards/models/list.js4
-rw-r--r--app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/boards/queries/group_lists_issues.query.graphql18
-rw-r--r--app/assets/javascripts/boards/queries/issue.fragment.graphql31
-rw-r--r--app/assets/javascripts/boards/queries/project_lists_issues.query.graphql18
-rw-r--r--app/assets/javascripts/boards/stores/actions.js37
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js39
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js5
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js18
-rw-r--r--app/assets/javascripts/boards/stores/state.js4
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql4
-rw-r--r--app/assets/javascripts/monitoring/components/alert_widget_form.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_panel.vue38
16 files changed, 225 insertions, 24 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 3178bda93b8..27f7edaab5c 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,7 +1,28 @@
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import ListIssue from 'ee_else_ce/boards/models/issue';
+
export function getMilestone() {
return null;
}
+export function formatListIssues(listIssues) {
+ return listIssues.nodes.reduce((map, list) => {
+ return {
+ ...map,
+ [list.id]: list.issues.nodes.map(
+ i =>
+ new ListIssue({
+ ...i,
+ id: getIdFromGraphQLId(i.id),
+ labels: i.labels?.nodes || [],
+ assignees: i.assignees?.nodes || [],
+ }),
+ ),
+ };
+ }, {});
+}
+
export default {
getMilestone,
+ formatListIssues,
};
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 6ac7fdce6a7..b7ca56e6a6f 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -42,7 +42,7 @@ export default {
},
},
computed: {
- ...mapState(['isShowingEpicsSwimlanes']),
+ ...mapState(['isShowingEpicsSwimlanes', 'boardLists']),
isSwimlanesOn() {
return this.glFeatures.boardsWithSwimlanes && this.isShowingEpicsSwimlanes;
},
@@ -73,11 +73,12 @@ export default {
<epics-swimlanes
v-else
ref="swimlanes"
- :lists="lists"
+ :lists="boardLists"
:can-admin-list="canAdminList"
:disabled="disabled"
:board-id="boardId"
:group-id="groupId"
+ :root-path="rootPath"
/>
</div>
</template>
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 7bc8a8a3b0e..3a0a1d4a484 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -117,7 +117,7 @@ export default () => {
boardId: this.boardId,
fullPath: $boardApp.dataset.fullPath,
};
- this.setEndpoints(endpoints);
+ this.setInitialBoardData({ ...endpoints, boardType: this.parent });
boardsStore.setEndpoints(endpoints);
boardsStore.rootPath = this.boardsEndpoint;
@@ -189,7 +189,7 @@ export default () => {
}
},
methods: {
- ...mapActions(['setEndpoints']),
+ ...mapActions(['setInitialBoardData']),
updateTokens() {
this.filterManager.updateTokens();
},
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 00f58ca0e98..bf25481cd69 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -60,7 +60,9 @@ class List {
this.title = this.milestone.title;
}
- if (!typeInfo.isBlank && this.id) {
+ // doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards
+ // Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/229416
+ if (!typeInfo.isBlank && this.id && !obj.doNotFetchIssues) {
this.getIssues().catch(() => {
// TODO: handle request error
});
diff --git a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql b/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
index 5b532906f6a..8abd79332fb 100644
--- a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
+++ b/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
position
listType
collapsed
+ maxIssueCount
label {
id
title
diff --git a/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql b/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
new file mode 100644
index 00000000000..724c7884c58
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
@@ -0,0 +1,18 @@
+#import "./issue.fragment.graphql"
+
+query GroupListIssues($fullPath: ID!, $boardId: ID!) {
+ group(fullPath: $fullPath) {
+ board(id: $boardId) {
+ lists {
+ nodes {
+ id
+ issues {
+ nodes {
+ ...IssueNode
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/issue.fragment.graphql b/app/assets/javascripts/boards/queries/issue.fragment.graphql
new file mode 100644
index 00000000000..89d56b895a4
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/issue.fragment.graphql
@@ -0,0 +1,31 @@
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+
+fragment IssueNode on Issue {
+ id
+ iid
+ title
+ referencePath: reference(full: true)
+ dueDate
+ timeEstimate
+ weight
+ confidential
+ webUrl
+ subscribed
+ blocked
+ epic {
+ id
+ }
+ assignees {
+ nodes {
+ ...User
+ }
+ }
+ labels {
+ nodes {
+ id
+ title
+ color
+ description
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql b/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
new file mode 100644
index 00000000000..149b76848ef
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
@@ -0,0 +1,18 @@
+#import "./issue.fragment.graphql"
+
+query ProjectListIssues($fullPath: ID!, $boardId: ID!) {
+ project(fullPath: $fullPath) {
+ board(id: $boardId) {
+ lists {
+ nodes {
+ id
+ issues {
+ nodes {
+ ...IssueNode
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 49d2a5c546f..b4be7546252 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,4 +1,11 @@
import * as types from './mutation_types';
+import createDefaultClient from '~/lib/graphql';
+import { BoardType } from '~/boards/constants';
+import { formatListIssues } from '../boards_util';
+import groupListsIssuesQuery from '../queries/group_lists_issues.query.graphql';
+import projectListsIssuesQuery from '../queries/project_lists_issues.query.graphql';
+
+const gqlClient = createDefaultClient();
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -6,8 +13,8 @@ const notImplemented = () => {
};
export default {
- setEndpoints: ({ commit }, endpoints) => {
- commit(types.SET_ENDPOINTS, endpoints);
+ setInitialBoardData: ({ commit }, data) => {
+ commit(types.SET_INITIAL_BOARD_DATA, data);
},
setActiveId({ commit }, id) {
@@ -38,6 +45,32 @@ export default {
notImplemented();
},
+ fetchIssuesForAllLists: ({ state, commit }) => {
+ commit(types.REQUEST_ISSUES_FOR_ALL_LISTS);
+
+ const { endpoints, boardType } = state;
+ const { fullPath, boardId } = endpoints;
+
+ const query = boardType === BoardType.group ? groupListsIssuesQuery : projectListsIssuesQuery;
+
+ const variables = {
+ fullPath,
+ boardId: `gid://gitlab/Board/${boardId}`,
+ };
+
+ return gqlClient
+ .query({
+ query,
+ variables,
+ })
+ .then(({ data }) => {
+ const { lists } = data[boardType]?.board;
+ const listIssues = formatListIssues(lists);
+ commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS, listIssues);
+ })
+ .catch(() => commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE));
+ },
+
moveIssue: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 8a51c5f4fcf..30c71d64085 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -81,7 +81,7 @@ const boardsStore = {
showPage(page) {
this.state.currentPage = page;
},
- addList(listObj) {
+ updateListPosition(listObj) {
const listType = listObj.listType || listObj.list_type;
let { position } = listObj;
if (listType === ListType.closed) {
@@ -91,6 +91,10 @@ const boardsStore = {
}
const list = new List({ ...listObj, position });
+ return list;
+ },
+ addList(listObj) {
+ const list = this.updateListPosition(listObj);
this.state.lists = sortBy([...this.state.lists, list], 'position');
return list;
},
@@ -850,19 +854,28 @@ const boardsStore = {
},
refreshIssueData(issue, obj) {
- issue.id = obj.id;
- issue.iid = obj.iid;
- issue.title = obj.title;
- issue.confidential = obj.confidential;
- issue.dueDate = obj.due_date;
- issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
- issue.referencePath = obj.reference_path;
- issue.path = obj.real_path;
- issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
+ // issue.id = obj.id;
+ // issue.iid = obj.iid;
+ // issue.title = obj.title;
+ // issue.confidential = obj.confidential;
+ // issue.dueDate = obj.due_date || obj.dueDate;
+ // issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
+ // issue.referencePath = obj.reference_path || obj.referencePath;
+ // issue.path = obj.real_path || obj.webUrl;
+ // issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
+ // issue.project_id = obj.project_id;
+ // issue.timeEstimate = obj.time_estimate || obj.timeEstimate;
+ // issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
+ // issue.blocked = obj.blocked;
+ // issue.epic = obj.epic;
+
+ const convertedObj = convertObjectPropsToCamelCase(obj, {
+ dropKeys: ['issue_sidebar_endpoint', 'real_path', 'webUrl'],
+ });
+ convertedObj.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
+ issue.path = obj.real_path || obj.webUrl;
issue.project_id = obj.project_id;
- issue.timeEstimate = obj.time_estimate;
- issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
- issue.blocked = obj.blocked;
+ Object.assign(issue, convertedObj);
if (obj.project) {
issue.project = new IssueProject(obj.project);
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 8deda166909..0f96dc2e287 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -1,4 +1,4 @@
-export const SET_ENDPOINTS = 'SET_ENDPOINTS';
+export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
@@ -8,6 +8,9 @@ export const RECEIVE_UPDATE_LIST_ERROR = 'RECEIVE_UPDATE_LIST_ERROR';
export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST';
export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS';
export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR';
+export const REQUEST_ISSUES_FOR_ALL_LISTS = 'REQUEST_ISSUES_FOR_ALL_LISTS';
+export const RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS = 'RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS';
+export const RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE = 'RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE';
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 0cf4080cf35..ca9b911ce5b 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -6,8 +6,10 @@ const notImplemented = () => {
};
export default {
- [mutationTypes.SET_ENDPOINTS]: (state, endpoints) => {
+ [mutationTypes.SET_INITIAL_BOARD_DATA]: (state, data) => {
+ const { boardType, ...endpoints } = data;
state.endpoints = endpoints;
+ state.boardType = boardType;
},
[mutationTypes.SET_ACTIVE_ID](state, id) {
@@ -50,6 +52,20 @@ export default {
notImplemented();
},
+ [mutationTypes.REQUEST_ISSUES_FOR_ALL_LISTS]: state => {
+ state.isLoadingIssues = true;
+ },
+
+ [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, listIssues) => {
+ state.issuesByListId = listIssues;
+ state.isLoadingIssues = false;
+ },
+
+ [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE]: state => {
+ state.listIssueFetchFailure = true;
+ state.isLoadingIssues = false;
+ },
+
[mutationTypes.REQUEST_ADD_ISSUE]: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index fa5dc7d663d..cb6930774ed 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -2,6 +2,10 @@ import { inactiveId } from '~/boards/constants';
export default () => ({
endpoints: {},
+ boardType: null,
isShowingLabels: true,
activeId: inactiveId,
+ issuesByListId: {},
+ isLoadingIssues: false,
+ listIssueFetchFailure: false,
});
diff --git a/app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql
new file mode 100644
index 00000000000..22bcefbecd3
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql
@@ -0,0 +1,4 @@
+fragment PageInfo on PageInfo {
+ startCursor
+ endCursor
+}
diff --git a/app/assets/javascripts/monitoring/components/alert_widget_form.vue b/app/assets/javascripts/monitoring/components/alert_widget_form.vue
index ef9dfce4aa9..f961e21984b 100644
--- a/app/assets/javascripts/monitoring/components/alert_widget_form.vue
+++ b/app/assets/javascripts/monitoring/components/alert_widget_form.vue
@@ -302,7 +302,6 @@ export default {
/>
</gl-form-group>
<gl-form-group
- v-if="glFeatures.alertRunbooks"
:label="s__('PrometheusAlerts|Runbook URL (optional)')"
label-for="alert-runbook"
>
@@ -312,6 +311,7 @@ export default {
:disabled="formDisabled"
data-testid="alertRunbookField"
type="text"
+ :placeholder="s__('PrometheusAlerts|https://gitlab.com/gitlab-com/runbooks')"
/>
</gl-form-group>
</div>
diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
index f57ea49626e..ad320b004bc 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
@@ -1,18 +1,20 @@
<script>
import { mapState } from 'vuex';
-import { pickBy } from 'lodash';
+import { mapValues, pickBy } from 'lodash';
import invalidUrl from '~/lib/utils/invalid_url';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { relativePathToAbsolute, getBaseURL, visitUrl, isSafeURL } from '~/lib/utils/url_utility';
import {
GlResizeObserverDirective,
GlIcon,
+ GlLink,
GlLoadingIcon,
GlNewDropdown as GlDropdown,
GlNewDropdownItem as GlDropdownItem,
GlNewDropdownDivider as GlDropdownDivider,
GlModal,
GlModalDirective,
+ GlSprintf,
GlTooltip,
GlTooltipDirective,
} from '@gitlab/ui';
@@ -44,12 +46,14 @@ export default {
MonitorEmptyChart,
AlertWidget,
GlIcon,
+ GlLink,
GlLoadingIcon,
GlTooltip,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlModal,
+ GlSprintf,
},
directives: {
GlResizeObserver: GlResizeObserverDirective,
@@ -341,6 +345,19 @@ export default {
this.$refs.copyChartLink.$el.firstChild.click();
}
},
+ getAlertRunbooks(queries) {
+ const hasRunbook = alert => Boolean(alert.runbookUrl);
+ const graphAlertsWithRunbooks = pickBy(this.getGraphAlerts(queries), hasRunbook);
+ const alertToRunbookTransform = alert => {
+ const alertQuery = queries.find(query => query.metricId === alert.metricId);
+ return {
+ key: alert.metricId,
+ href: alert.runbookUrl,
+ label: alertQuery.label,
+ };
+ };
+ return mapValues(graphAlertsWithRunbooks, alertToRunbookTransform);
+ },
},
panelTypes,
};
@@ -436,6 +453,25 @@ export default {
>
{{ __('Alerts') }}
</gl-dropdown-item>
+ <gl-dropdown-item
+ v-for="runbook in getAlertRunbooks(graphData.metrics)"
+ :key="runbook.key"
+ :href="safeUrl(runbook.href)"
+ data-testid="runbookLink"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <span class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
+ <span>
+ <gl-sprintf :message="s__('Metrics|View runbook - %{label}')">
+ <template #label>
+ {{ runbook.label }}
+ </template>
+ </gl-sprintf>
+ </span>
+ <gl-icon name="external-link" />
+ </span>
+ </gl-dropdown-item>
<template v-if="graphData.links && graphData.links.length">
<gl-dropdown-divider />