diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js index afdc4a7d55e..2e976cd6230 100644 --- a/app/assets/javascripts/observability/client.js +++ b/app/assets/javascripts/observability/client.js @@ -189,11 +189,57 @@ async function fetchTraces(tracingUrl, { filters = {}, pageToken, pageSize } = { } } -export function buildClient({ provisioningUrl, tracingUrl }) { +async function fetchServices(servicesUrl) { + try { + const { data } = await axios.get(servicesUrl, { + withCredentials: true, + }); + + if (!Array.isArray(data.services)) { + throw new Error('failed to fetch services. invalid response'); // eslint-disable-line @gitlab/require-i18n-strings + } + + return data.services; + } catch (e) { + return reportErrorAndThrow(e); + } +} + +async function fetchOperations(operationsUrl, serviceName) { + try { + if (!serviceName) { + throw new Error('fetchOperations() - serviceName is required.'); + } + if (!operationsUrl.includes('$SERVICE_NAME$')) { + throw new Error('fetchOperations() - operationsUrl must contain $SERVICE_NAME$'); + } + const url = operationsUrl.replace('$SERVICE_NAME$', serviceName); + const { data } = await axios.get(url, { + withCredentials: true, + }); + + if (!Array.isArray(data.operations)) { + throw new Error('failed to fetch operations. invalid response'); // eslint-disable-line @gitlab/require-i18n-strings + } + + return data.operations; + } catch (e) { + return reportErrorAndThrow(e); + } +} + +export function buildClient({ provisioningUrl, tracingUrl, servicesUrl, operationsUrl } = {}) { + if (!provisioningUrl || !tracingUrl || !servicesUrl || !operationsUrl) { + throw new Error( + 'missing required params. provisioningUrl, tracingUrl, servicesUrl, operationsUrl are required', + ); + } return { enableTraces: () => enableTraces(provisioningUrl), isTracingEnabled: () => isTracingEnabled(provisioningUrl), fetchTraces: (filters) => fetchTraces(tracingUrl, filters), fetchTrace: (traceId) => fetchTrace(tracingUrl, traceId), + fetchServices: () => fetchServices(servicesUrl), + fetchOperations: (serviceName) => fetchOperations(operationsUrl, serviceName), }; } diff --git a/app/assets/javascripts/observability/components/observability_container.vue b/app/assets/javascripts/observability/components/observability_container.vue index b7697cea299..1518c132560 100644 --- a/app/assets/javascripts/observability/components/observability_container.vue +++ b/app/assets/javascripts/observability/components/observability_container.vue @@ -13,11 +13,19 @@ export default { type: String, required: true, }, + provisioningUrl: { + type: String, + required: true, + }, tracingUrl: { type: String, required: true, }, - provisioningUrl: { + servicesUrl: { + type: String, + required: true, + }, + operationsUrl: { type: String, required: true, }, @@ -58,6 +66,8 @@ export default { this.observabilityClient = buildClient({ provisioningUrl: this.provisioningUrl, tracingUrl: this.tracingUrl, + servicesUrl: this.servicesUrl, + operationsUrl: this.operationsUrl, }); this.$refs.observabilitySkeleton?.onContentLoaded(); } else if (status === 'error') { diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue index a3282932f84..ee9edd6a022 100644 --- a/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue +++ b/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue @@ -1,5 +1,6 @@ + + + + + + diff --git a/app/assets/javascripts/vue_merge_request_widget/components/checks/message.vue b/app/assets/javascripts/vue_merge_request_widget/components/checks/message.vue new file mode 100644 index 00000000000..d0d749aa441 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/checks/message.vue @@ -0,0 +1,44 @@ + + + + + + + + {{ check.failureReason }} + + + + + diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js new file mode 100644 index 00000000000..1c57226f887 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js @@ -0,0 +1,91 @@ +import createMockApollo from 'helpers/mock_apollo_helper'; +import mergeChecksQuery from '../queries/merge_checks.query.graphql'; +import conflictsStateQuery from '../queries/states/conflicts.query.graphql'; +import MergeChecks from './merge_checks.vue'; + +const stylesheetsRequireCtx = require.context( + '../../../stylesheets', + true, + /(page_bundles\/merge_requests)\.scss$/, +); + +stylesheetsRequireCtx('./page_bundles/merge_requests.scss'); + +const defaultRender = (apolloProvider) => ({ + components: { MergeChecks }, + apolloProvider, + data() { + return { mr: { conflictResolutionPath: 'https://gitlab.com' } }; + }, + template: '', +}); + +const Template = ({ canMerge, failed, pushToSourceBranch }) => { + const requestHandlers = [ + [ + mergeChecksQuery, + () => + Promise.resolve({ + data: { + project: { + id: 1, + mergeRequest: { + id: 1, + userPermissions: { canMerge }, + mergeChecks: [ + { + failureReason: 'Unresolved discussions', + identifier: 'unresolved_discussions', + result: failed ? 'failed' : 'passed', + }, + { + failureReason: 'Resolve conflicts', + identifier: 'conflicts', + result: failed ? 'failed' : 'passed', + }, + ], + }, + }, + }, + }), + ], + [ + conflictsStateQuery, + () => + Promise.resolve({ + data: { + project: { + id: 1, + mergeRequest: { + id: 1, + shouldBeRebased: false, + sourceBranchProtected: false, + userPermissions: { pushToSourceBranch }, + }, + }, + }, + }), + ], + ]; + const apolloProvider = createMockApollo(requestHandlers); + + return defaultRender(apolloProvider); +}; + +const LoadingTemplate = () => { + const requestHandlers = [[mergeChecksQuery, () => new Promise(() => {})]]; + const apolloProvider = createMockApollo(requestHandlers); + + return defaultRender(apolloProvider); +}; + +export const Default = Template.bind({}); +Default.args = { canMerge: true, failed: true, pushToSourceBranch: true }; + +export const Loading = LoadingTemplate.bind({}); +Loading.args = {}; + +export default { + title: 'vue_merge_request_widget/merge_checks', + component: MergeChecks, +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue new file mode 100644 index 00000000000..fa84c0a4a6f --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue index dd899701de0..2a18af90495 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue @@ -1,5 +1,6 @@